PHP 设计模式系列 -- 责任链模式(Chain Of Responsibilities)

WBOY
Release: 2016-06-20 12:41:45
Original
864 people have browsed it

1、模式定义

责任链模式将处理请求的对象连成一条链,沿着这条链传递该请求,直到有一个对象处理请求为止,这使得多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。

责任链模式在现实中使用的很多,常见的就是 OA 系统中的工作流。

2、UML 类图

3、示例代码

Request.php

<?phpnamespace DesignPatterns\Behavioral\ChainOfResponsibilities;/** * 经过责任链的Request类 * * 关于请求: 有时候,不需要一个请求对象,只需一个整型数据或者一个数组即可。 * 但是作为一个完整示例,这里我们生成了一个请求类。 * 在实际项目中,也推荐使用请求类,即是是一个标准类\stdClass, * 因为这样的话代码更具扩展性,因为责任链的处理器并不了解外部世界, * 如果某天你想要添加其它复杂处理时不使用请求类会很麻烦 */class Request{    // getter and setter but I don't want to generate too much noise in handlers}
Copy after login

Handler.php

<?phpnamespace DesignPatterns\Behavioral\ChainOfResponsibilities;/** * 责任链的通用处理器类Handler(通常是一个接口或抽象类) * * Yes you could have a lighter CoR with a simpler handler but if you want your CoR * to be extendable and decoupled, it's a better idea to do things like that in real * situations. Usually, a CoR is meant to be changed everytime and evolves, that's * why we slice the workflow in little bits of code. */abstract class Handler{    /**     * @var Handler     */    private $successor = null;    /**     * 追加处理类到责任链     *     * A prepend method could be done with the same spirit     *     * You could also send the successor in the constructor but in PHP that is a     * bad idea because you have to remove the type-hint of the parameter because     * the last handler has a null successor.     *     * And if you override the constructor, that Handler can no longer have a     * successor. One solution is to provide a NullObject (see pattern).     * It is more preferable to keep the constructor "free" to inject services     * you need with the DiC of symfony2 for example.     *     * @param Handler $handler     */    final public function append(Handler $handler)    {        if (is_null($this->successor)) {            $this->successor = $handler;        } else {            $this->successor->append($handler);        }    }    /**     * 处理请求     *     * This approach by using a template method pattern ensures you that     * each subclass will not forget to call the successor. Besides, the returned     * boolean value indicates you if the request have been processed or not.     *     * @param Request $req     *     * @return bool     */    final public function handle(Request $req)    {        $req->forDebugOnly = get_called_class();        $processed = $this->processing($req);        if (!$processed) {            // the request has not been processed by this handler => see the next            if (!is_null($this->successor)) {                $processed = $this->successor->handle($req);            }        }        return $processed;    }    /**     * 每个处理器具体实现类都要实现这个方法对请求进行处理     *     * @param Request $req     *     * @return bool true if the request has been processed     */    abstract protected function processing(Request $req);}
Copy after login

Responsible/SlowStorage.php

<?phpnamespace DesignPatterns\Behavioral\ChainOfResponsibilities\Responsible;use DesignPatterns\Behavioral\ChainOfResponsibilities\Handler;use DesignPatterns\Behavioral\ChainOfResponsibilities\Request;/** * This is mostly the same code as FastStorage but in fact, it may greatly differs * * One important fact about CoR: each item in the chain MUST NOT assume its position * in the chain. A CoR is not responsible if the request is not handled UNLESS * you make an "ExceptionHandler" which throws exception if the request goes there. * * To be really extendable, each handler doesn't know if there is something after it. * */class SlowStorage extends Handler{    /**     * @var array     */    protected $data = array();    /**     * @param array $data     */    public function __construct($data = array())    {        $this->data = $data;    }    protected function processing(Request $req)    {        if ('get' === $req->verb) {            if (array_key_exists($req->key, $this->data)) {                $req->response = $this->data[$req->key];                return true;            }        }        return false;    }}
Copy after login

Responsible/FastStorage.php

<?phpnamespace DesignPatterns\Behavioral\ChainOfResponsibilities\Responsible;use DesignPatterns\Behavioral\ChainOfResponsibilities\Handler;use DesignPatterns\Behavioral\ChainOfResponsibilities\Request;/** * Class FastStorage */class FastStorage extends Handler{    /**     * @var array     */    protected $data = array();    /**     * @param array $data     */    public function __construct($data = array())    {        $this->data = $data;    }    protected function processing(Request $req)    {        if ('get' === $req->verb) {            if (array_key_exists($req->key, $this->data)) {                // the handler IS responsible and then processes the request                $req->response = $this->data[$req->key];                // instead of returning true, I could return the value but it proves                // to be a bad idea. What if the value IS "false" ?                return true;            }        }        return false;    }}
Copy after login

4、测试代码

Tests/ChainTest.php

<?phpnamespace DesignPatterns\Behavioral\ChainOfResponsibilities\Tests;use DesignPatterns\Behavioral\ChainOfResponsibilities\Request;use DesignPatterns\Behavioral\ChainOfResponsibilities\Responsible\FastStorage;use DesignPatterns\Behavioral\ChainOfResponsibilities\Responsible\SlowStorage;use DesignPatterns\Behavioral\ChainOfResponsibilities\Responsible;/** * ChainTest tests the CoR */class ChainTest extends \PHPUnit_Framework_TestCase{    /**     * @var FastStorage     */    protected $chain;    protected function setUp()    {        $this->chain = new FastStorage(array('bar' => 'baz'));        $this->chain->append(new SlowStorage(array('bar' => 'baz', 'foo' => 'bar')));    }    public function makeRequest()    {        $request = new Request();        $request->verb = 'get';        return array(            array($request)        );    }    /**     * @dataProvider makeRequest     */    public function testFastStorage($request)    {        $request->key = 'bar';        $ret = $this->chain->handle($request);        $this->assertTrue($ret);        $this->assertObjectHasAttribute('response', $request);        $this->assertEquals('baz', $request->response);        // despite both handle owns the 'bar' key, the FastStorage is responding first        $className = 'DesignPatterns\Behavioral\ChainOfResponsibilities\Responsible\FastStorage';        $this->assertEquals($className, $request->forDebugOnly);    }    /**     * @dataProvider makeRequest     */    public function testSlowStorage($request)    {        $request->key = 'foo';        $ret = $this->chain->handle($request);        $this->assertTrue($ret);        $this->assertObjectHasAttribute('response', $request);        $this->assertEquals('bar', $request->response);        // FastStorage has no 'foo' key, the SlowStorage is responding        $className = 'DesignPatterns\Behavioral\ChainOfResponsibilities\Responsible\SlowStorage';        $this->assertEquals($className, $request->forDebugOnly);    }    /**     * @dataProvider makeRequest     */    public function testFailure($request)    {        $request->key = 'kurukuku';        $ret = $this->chain->handle($request);        $this->assertFalse($ret);        // the last responsible :        $className = 'DesignPatterns\Behavioral\ChainOfResponsibilities\Responsible\SlowStorage';        $this->assertEquals($className, $request->forDebugOnly);    }}
Copy after login

5、总结

责任链模式的主要优点在于可以降低系统的耦合度,简化对象的相互连接,同时增强给对象指派职责的灵活性,增加新的请求处理类也很方便;其主要缺点在于不能保证请求一定被接收,且对于比较长的职责链,请求的处理可能涉及到多个处理对象,系统性能将受到一定影响,而且在进行代码调试时不太方便。

source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template