search
HomeBackend DevelopmentPHP TutorialPHP 设计模式系列 -- 责任链模式(Chain Of Responsibilities)

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、总结

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

Statement
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
Working with Flash Session Data in LaravelWorking with Flash Session Data in LaravelMar 12, 2025 pm 05:08 PM

Laravel simplifies handling temporary session data using its intuitive flash methods. This is perfect for displaying brief messages, alerts, or notifications within your application. Data persists only for the subsequent request by default: $request-

cURL in PHP: How to Use the PHP cURL Extension in REST APIscURL in PHP: How to Use the PHP cURL Extension in REST APIsMar 14, 2025 am 11:42 AM

The PHP Client URL (cURL) extension is a powerful tool for developers, enabling seamless interaction with remote servers and REST APIs. By leveraging libcurl, a well-respected multi-protocol file transfer library, PHP cURL facilitates efficient execution of various network protocols, including HTTP, HTTPS, and FTP. This extension offers granular control over HTTP requests, supports multiple concurrent operations, and provides built-in security features.

PHP Logging: Best Practices for PHP Log AnalysisPHP Logging: Best Practices for PHP Log AnalysisMar 10, 2025 pm 02:32 PM

PHP logging is essential for monitoring and debugging web applications, as well as capturing critical events, errors, and runtime behavior. It provides valuable insights into system performance, helps identify issues, and supports faster troubleshoot

Simplified HTTP Response Mocking in Laravel TestsSimplified HTTP Response Mocking in Laravel TestsMar 12, 2025 pm 05:09 PM

Laravel provides concise HTTP response simulation syntax, simplifying HTTP interaction testing. This approach significantly reduces code redundancy while making your test simulation more intuitive. The basic implementation provides a variety of response type shortcuts: use Illuminate\Support\Facades\Http; Http::fake([ 'google.com' => 'Hello World', 'github.com' => ['foo' => 'bar'], 'forge.laravel.com' =>

12 Best PHP Chat Scripts on CodeCanyon12 Best PHP Chat Scripts on CodeCanyonMar 13, 2025 pm 12:08 PM

Do you want to provide real-time, instant solutions to your customers' most pressing problems? Live chat lets you have real-time conversations with customers and resolve their problems instantly. It allows you to provide faster service to your custom

Explain the concept of late static binding in PHP.Explain the concept of late static binding in PHP.Mar 21, 2025 pm 01:33 PM

Article discusses late static binding (LSB) in PHP, introduced in PHP 5.3, allowing runtime resolution of static method calls for more flexible inheritance.Main issue: LSB vs. traditional polymorphism; LSB's practical applications and potential perfo

Customizing/Extending Frameworks: How to add custom functionality.Customizing/Extending Frameworks: How to add custom functionality.Mar 28, 2025 pm 05:12 PM

The article discusses adding custom functionality to frameworks, focusing on understanding architecture, identifying extension points, and best practices for integration and debugging.

See all articles

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

AI Hentai Generator

AI Hentai Generator

Generate AI Hentai for free.

Hot Article

R.E.P.O. Energy Crystals Explained and What They Do (Yellow Crystal)
3 weeks agoBy尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Best Graphic Settings
3 weeks agoBy尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. How to Fix Audio if You Can't Hear Anyone
3 weeks agoBy尊渡假赌尊渡假赌尊渡假赌

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

PhpStorm Mac version

PhpStorm Mac version

The latest (2018.2.1) professional PHP integrated development tool

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

EditPlus Chinese cracked version

EditPlus Chinese cracked version

Small size, syntax highlighting, does not support code prompt function

mPDF

mPDF

mPDF is a PHP library that can generate PDF files from UTF-8 encoded HTML. The original author, Ian Back, wrote mPDF to output PDF files "on the fly" from his website and handle different languages. It is slower than original scripts like HTML2FPDF and produces larger files when using Unicode fonts, but supports CSS styles etc. and has a lot of enhancements. Supports almost all languages, including RTL (Arabic and Hebrew) and CJK (Chinese, Japanese and Korean). Supports nested block-level elements (such as P, DIV),