首页  >  文章  >  后端开发  >  PHP 设计模式系列 -- 责任链模式(Chain Of Responsibilities)

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

WBOY
WBOY原创
2016-06-20 12:41:45836浏览

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}

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);}

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;    }}

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;    }}

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);    }}

5、总结

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

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn