PHP デザイン パターン シリーズ -- 責任の連鎖
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. まとめ
責任連鎖モデルの主な利点は、システムの結合を軽減し、オブジェクトの相互接続を簡素化し、同時にシステムの相互接続を強化できることです。オブジェクトに責任を割り当てたり、新しいものを追加したりできる柔軟性。リクエスト処理クラスも非常に便利です。その主な欠点は、リクエストが受信されることを保証できないことと、責任の連鎖が比較的長い場合、リクエストの処理に複数の処理が含まれる可能性があることです。オブジェクトを処理するため、システムのパフォーマンスにある程度の影響があり、コードのデバッグにはあまり便利ではありません。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック









PHPクライアントURL(CURL)拡張機能は、開発者にとって強力なツールであり、リモートサーバーやREST APIとのシームレスな対話を可能にします。尊敬されるマルチプロトコルファイル転送ライブラリであるLibcurlを活用することにより、PHP Curlは効率的なexecuを促進します

記事では、PHP 5.3で導入されたPHPの後期静的結合(LSB)について説明し、より柔軟な継承を求める静的メソッドコールのランタイム解像度を可能にします。 LSBの実用的なアプリケーションと潜在的なパフォーマ

JWTは、JSONに基づくオープン標準であり、主にアイデンティティ認証と情報交換のために、当事者間で情報を安全に送信するために使用されます。 1。JWTは、ヘッダー、ペイロード、署名の3つの部分で構成されています。 2。JWTの実用的な原則には、JWTの生成、JWTの検証、ペイロードの解析という3つのステップが含まれます。 3. PHPでの認証にJWTを使用する場合、JWTを生成および検証でき、ユーザーの役割と許可情報を高度な使用に含めることができます。 4.一般的なエラーには、署名検証障害、トークンの有効期限、およびペイロードが大きくなります。デバッグスキルには、デバッグツールの使用とロギングが含まれます。 5.パフォーマンスの最適化とベストプラクティスには、適切な署名アルゴリズムの使用、有効期間を合理的に設定することが含まれます。

記事では、入力検証、認証、定期的な更新など、脆弱性から保護するためのフレームワークの重要なセキュリティ機能について説明します。

PHP開発でPHPのCurlライブラリを使用してJSONデータを送信すると、外部APIと対話する必要があることがよくあります。一般的な方法の1つは、Curlライブラリを使用して投稿を送信することです。

この記事では、フレームワークにカスタム機能を追加し、アーキテクチャの理解、拡張ポイントの識別、統合とデバッグのベストプラクティスに焦点を当てています。

ReactPhpの詳細な解釈の非ブロッキング機能の公式紹介は、多くの開発者の質問を呼び起こしました。
