文章来源refactoringguru.cn/design-patt…
PHP 责任链模式讲解和代码示例
责任链是一种行为设计模式, 允许你将请求沿着处理者链进行发送, 直至其中一个处理者对其进行处理。
该模式允许多个对象来对请求进行处理, 而无需让发送者类与具体接收者类相耦合。 链可在运行时由遵循标准处理者接口的任意处理者动态生成。
复杂度:******
流行度:******
使用示例: 责任链模式在 PHP 程序中并不常见, 因为它仅在代码与对象链打交道时才能发挥作用。 可以说在 PHP 中, 该模式最著名的例子之一是 PSR-15 中所描述的 HTTP 请求中间件
识别方法: 该模式可通过一组对象的行为方法间接调用其他对象的相同方法来识别, 而且所有对象都会遵循相同的接口。
概念示例
本例说明了责任链设计模式的结构并重点回答了下面的问题:
- 它由哪些类组成?
- 这些类扮演了哪些角色?
- 模式中的各个元素会以何种方式相互关联?
了解该模式的结构后, 你可以更容易地理解下面基于真实世界的 PHP 应用案例。
** index.php: 概念示例
<?php
namespace RefactoringGuru\ChainOfResponsibility\Conceptual;
/**
* The Handler interface declares a method for building the chain of handlers.
* It also declares a method for executing a request.
*/
interface Handler
{
public function setNext(Handler $handler): Handler;
public function handle(string $request): ?string;
}
/**
* The default chaining behavior can be implemented inside a base handler class.
*/
abstract class AbstractHandler implements Handler
{
/**
* @var Handler
*/
private $nextHandler;
public function setNext(Handler $handler): Handler
{
$this->nextHandler = $handler;
// Returning a handler from here will let us link handlers in a
// convenient way like this:
// $monkey->setNext($squirrel)->setNext($dog);
return $handler;
}
public function handle(string $request): ?string
{
if ($this->nextHandler) {
return $this->nextHandler->handle($request);
}
return null;
}
}
/**
* All Concrete Handlers either handle a request or pass it to the next handler
* in the chain.
*/
class MonkeyHandler extends AbstractHandler
{
public function handle(string $request): ?string
{
if ($request === "Banana") {
return "Monkey: I'll eat the " . $request . ".\n";
} else {
return parent::handle($request);
}
}
}
class SquirrelHandler extends AbstractHandler
{
public function handle(string $request): ?string
{
if ($request === "Nut") {
return "Squirrel: I'll eat the " . $request . ".\n";
} else {
return parent::handle($request);
}
}
}
class DogHandler extends AbstractHandler
{
public function handle(string $request): ?string
{
if ($request === "MeatBall") {
return "Dog: I'll eat the " . $request . ".\n";
} else {
return parent::handle($request);
}
}
}
/**
* The client code is usually suited to work with a single handler. In most
* cases, it is not even aware that the handler is part of a chain.
*/
function clientCode(Handler $handler)
{
foreach (["Nut", "Banana", "Cup of coffee"] as $food) {
echo "Client: Who wants a " . $food . "?\n";
$result = $handler->handle($food);
if ($result) {
echo " " . $result;
} else {
echo " " . $food . " was left untouched.\n";
}
}
}
/**
* The other part of the client code constructs the actual chain.
*/
$monkey = new MonkeyHandler();
$squirrel = new SquirrelHandler();
$dog = new DogHandler();
$monkey->setNext($squirrel)->setNext($dog);
/**
* The client should be able to send a request to any handler, not just the
* first one in the chain.
*/
echo "Chain: Monkey > Squirrel > Dog\n\n";
clientCode($monkey);
echo "\n";
echo "Subchain: Squirrel > Dog\n\n";
clientCode($squirrel);
** Output.txt: 执行结果
Chain: Monkey > Squirrel > Dog
Client: Who wants a Nut?
Squirrel: I'll eat the Nut.
Client: Who wants a Banana?
Monkey: I'll eat the Banana.
Client: Who wants a Cup of coffee?
Cup of coffee was left untouched.
Subchain: Squirrel > Dog
Client: Who wants a Nut?
Squirrel: I'll eat the Nut.
Client: Who wants a Banana?
Banana was left untouched.
Client: Who wants a Cup of coffee?
Cup of coffee was left untouched.