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

Outils d'IA chauds

Undresser.AI Undress
Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover
Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool
Images de déshabillage gratuites

Clothoff.io
Dissolvant de vêtements AI

AI Hentai Generator
Générez AI Hentai gratuitement.

Article chaud

Outils chauds

Bloc-notes++7.3.1
Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise
Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1
Puissant environnement de développement intégré PHP

Dreamweaver CS6
Outils de développement Web visuel

SublimeText3 version Mac
Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Sujets chauds

Les longues URL, souvent encombrées de mots clés et de paramètres de suivi, peuvent dissuader les visiteurs. Un script de raccourcissement d'URL offre une solution, créant des liens concis idéaux pour les médias sociaux et d'autres plateformes. Ces scripts sont utiles pour les sites Web individuels

À la suite de son acquisition de haut niveau par Facebook en 2012, Instagram a adopté deux ensembles d'API pour une utilisation tierce. Ce sont l'API graphique Instagram et l'API d'affichage de base Instagram. En tant que développeur créant une application qui nécessite des informations à partir d'un

Laravel simplifie la gestion des données de session temporaires à l'aide de ses méthodes de flash intuitives. Ceci est parfait pour afficher de brefs messages, alertes ou notifications dans votre application. Les données ne persistent que pour la demande ultérieure par défaut: $ demande-

Il s'agit de la deuxième et dernière partie de la série sur la construction d'une application React avec un back-end Laravel. Dans la première partie de la série, nous avons créé une API RESTful utilisant Laravel pour une application de liste de base sur le produit. Dans ce tutoriel, nous serons Dev

Laravel fournit une syntaxe de simulation de réponse HTTP concise, simplifiant les tests d'interaction HTTP. Cette approche réduit considérablement la redondance du code tout en rendant votre simulation de test plus intuitive. L'implémentation de base fournit une variété de raccourcis de type de réponse: Utiliser illuminate \ support \ faades \ http; Http :: faux ([[ 'google.com' => 'Hello World', 'github.com' => ['foo' => 'bar'], 'forge.laravel.com' =>

L'extension PHP Client URL (CURL) est un outil puissant pour les développeurs, permettant une interaction transparente avec des serveurs distants et des API REST. En tirant parti de Libcurl, une bibliothèque de transfert de fichiers multi-protocol très respectée, PHP Curl facilite Efficient Execu

Voulez-vous fournir des solutions instantanées en temps réel aux problèmes les plus pressants de vos clients? Le chat en direct vous permet d'avoir des conversations en temps réel avec les clients et de résoudre leurs problèmes instantanément. Il vous permet de fournir un service plus rapide à votre personnalité

L'enquête sur le paysage PHP 2025 étudie les tendances actuelles de développement du PHP. Il explore l'utilisation du cadre, les méthodes de déploiement et les défis, visant à fournir des informations aux développeurs et aux entreprises. L'enquête prévoit la croissance de la PHP moderne versio
