职责链模式 目的 建立一个对象链,按顺序处理请求。如果一个对象不能处理请求,就将调用委托给职责链中的下一个对象,以此类推。
UML 类图
代码 Handler.php 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <?php declare (strict_types = 1 );namespace DesignPatterns \Behavioral \ChainOfResponsibility ;use Psr \Http \Message \RequestInterface ;abstract class Handler { private ?Handler $successor = null ; public function __construct (Handler $handler = null ) { $this ->successor = $handler ; } final public function handle (RequestInterface $request ): ?string { $processed = $this ->processing ($request ); if ($processed === null && $this ->successor !== null ) { $processed = $this ->successor->handle ($request ); } return $processed ; } abstract protected function processing (RequestInterface $request ): ?string ; }
Responsible/FastStorage.php 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <?php declare (strict_types = 1 );namespace DesignPatterns \Behavioral \ChainOfResponsibility \Responsible ;use DesignPatterns \Behavioral \ChainOfResponsibility \Handler ;use Psr \Http \Message \RequestInterface ;class HttpInMemoryCacheHandler extends Handler { private array $data ; public function __construct (array $data , ?Handler $successor = null ) { parent ::__construct ($successor ); $this ->data = $data ; } protected function processing (RequestInterface $request ): ?string { $key = sprintf ( '%s?%s' , $request ->getUri ()->getPath (), $request ->getUri ()->getQuery () ); if ($request ->getMethod () == 'GET' && isset ($this ->data[$key ])) { return $this ->data[$key ]; } return null ; } }
Responsible/SlowStorage.php 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?php declare (strict_types = 1 );namespace DesignPatterns \Behavioral \ChainOfResponsibility \Responsible ;use DesignPatterns \Behavioral \ChainOfResponsibility \Handler ;use Psr \Http \Message \RequestInterface ;class SlowDatabaseHandler extends Handler { protected function processing (RequestInterface $request ): ?string { return 'Hello World!' ; } }
测试 Tests/ChainTest.php 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 <?php declare (strict_types = 1 );namespace DesignPatterns \Behavioral \ChainOfResponsibility \Tests ;use DesignPatterns \Behavioral \ChainOfResponsibility \Handler ;use DesignPatterns \Behavioral \ChainOfResponsibility \Responsible \HttpInMemoryCacheHandler ;use DesignPatterns \Behavioral \ChainOfResponsibility \Responsible \SlowDatabaseHandler ;use PHPUnit \Framework \TestCase ;use Psr \Http \Message \RequestInterface ;use Psr \Http \Message \UriInterface ;class ChainTest extends TestCase { private Handler $chain ; protected function setUp ( ): void { $this ->chain = new HttpInMemoryCacheHandler ( ['/foo/bar?index=1' => 'Hello In Memory!' ], new SlowDatabaseHandler () ); } public function testCanRequestKeyInFastStorage ( ) { $uri = $this ->createMock (UriInterface ::class ); $uri ->method ('getPath' )->willReturn ('/foo/bar' ); $uri ->method ('getQuery' )->willReturn ('index=1' ); $request = $this ->createMock (RequestInterface ::class ); $request ->method ('getMethod' ) ->willReturn ('GET' ); $request ->method ('getUri' )->willReturn ($uri ); $this ->assertSame ('Hello In Memory!' , $this ->chain->handle ($request )); } public function testCanRequestKeyInSlowStorage ( ) { $uri = $this ->createMock (UriInterface ::class ); $uri ->method ('getPath' )->willReturn ('/foo/baz' ); $uri ->method ('getQuery' )->willReturn ('' ); $request = $this ->createMock (RequestInterface ::class ); $request ->method ('getMethod' ) ->willReturn ('GET' ); $request ->method ('getUri' )->willReturn ($uri ); $this ->assertSame ('Hello World!' , $this ->chain->handle ($request )); } }