责任链模式(Chain of Responsibility)
前言
责任链模式将请求沿着处理者链传递,每个处理者决定处理请求或传递给下一个。它是中间件、事件冒泡、审批流程的核心模式。本文将详细讲解责任链模式的核心原理及实际应用。
一、核心概念
1.1 定义
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。
1.2 核心角色
| 角色 | 说明 |
|---|---|
| 处理者接口(Handler) | 定义处理请求的方法和后继者 |
| 具体处理者(ConcreteHandler) | 处理请求或传递给下一个 |
| 客户端(Client) | 创建链并触发请求 |
1.3 链式变体
| 类型 | 说明 |
|---|---|
| 纯责任链 | 只有一个处理者处理请求 |
| 不纯责任链 | 多个处理者可处理部分请求 |
| 中断型 | 处理后中断链 |
| 传递型 | 处理后继续传递 |
二、代码实现
2.1 基础实现
php
<?php
// 处理者接口
abstract class Handler
{
protected ?Handler $next = null;
public function setNext(Handler $handler): Handler
{
$this->next = $handler;
return $handler; // 支持链式调用
}
public function handle(string $request): ?string
{
if ($this->next !== null) {
return $this->next->handle($request);
}
return null;
}
}
// 具体处理者 A
class ConcreteHandlerA extends Handler
{
public function handle(string $request): ?string
{
if ($request === "A") {
return "Handler A: processing {$request}";
}
return parent::handle($request);
}
}
// 具体处理者 B
class ConcreteHandlerB extends Handler
{
public function handle(string $request): ?string
{
if ($request === "B") {
return "Handler B: processing {$request}";
}
return parent::handle($request);
}
}
// 具体处理者 C
class ConcreteHandlerC extends Handler
{
public function handle(string $request): ?string
{
if ($request === "C") {
return "Handler C: processing {$request}";
}
return parent::handle($request);
}
}
// 使用:构建链
$handlerA = new ConcreteHandlerA();
$handlerA->setNext(new ConcreteHandlerB())->setNext(new ConcreteHandlerC());
echo $handlerA->handle("A") . PHP_EOL; // Handler A: processing A
echo $handlerA->handle("B") . PHP_EOL; // Handler B: processing B
echo $handlerA->handle("C") . PHP_EOL; // Handler C: processing C
var_dump($handlerA->handle("D")); // null(无人处理)2.2 实际案例:审批流程
php
<?php
// 请求:请假申请
class LeaveRequest
{
public function __construct(
public string $employee,
public int $days,
public string $reason
) {}
}
// 处理者:审批人
abstract class Approver
{
protected ?Approver $next = null;
protected string $name;
protected int $maxDays;
public function __construct(string $name, int $maxDays)
{
$this->name = $name;
$this->maxDays = $maxDays;
}
public function setNext(Approver $approver): Approver
{
$this->next = $approver;
return $approver;
}
public function approve(LeaveRequest $request): string
{
if ($request->days <= $this->maxDays) {
return "{$this->name} approved {$request->days} days for {$request->employee}";
}
if ($this->next !== null) {
return $this->next->approve($request);
}
return "No one can approve {$request->days} days";
}
}
// 组长:最多 3 天
class TeamLead extends Approver
{
public function __construct()
{
parent::__construct("Team Lead", 3);
}
}
// 经理:最多 7 天
class Manager extends Approver
{
public function __construct()
{
parent::__construct("Manager", 7);
}
}
// 总监:最多 30 天
class Director extends Approver
{
public function __construct()
{
parent::__construct("Director", 30);
}
}
// CEO:无上限
class CEO extends Approver
{
public function __construct()
{
parent::__construct("CEO", PHP_INT_MAX);
}
}
// 使用:构建审批链
$teamLead = new TeamLead();
$teamLead->setNext(new Manager())->setNext(new Director())->setNext(new CEO());
echo $teamLead->approve(new LeaveRequest("Flynn", 2, "Sick")) . PHP_EOL;
// Team Lead approved 2 days for Flynn
echo $teamLead->approve(new LeaveRequest("Alice", 5, "Vacation")) . PHP_EOL;
// Manager approved 5 days for Alice
echo $teamLead->approve(new LeaveRequest("Bob", 15, "Marriage")) . PHP_EOL;
// Director approved 15 days for Bob
echo $teamLead->approve(new LeaveRequest("Charlie", 60, "Sabbatical")) . PHP_EOL;
// CEO approved 60 days for Charlie2.3 实际案例:Web 中间件(Laravel 风格)
php
<?php
// 中间件接口
interface Middleware
{
public function handle($request, Closure $next);
}
class MiddlewareChain
{
/** @var callable[] */
private array $middlewares = [];
public function use(callable $middleware): self
{
$this->middlewares[] = $middleware;
return $this;
}
public function execute($request): void
{
$dispatch = function (int $index) use ($request, &$dispatch) {
if ($index >= count($this->middlewares)) return;
$middleware = $this->middlewares[$index];
$middleware($request, function () use ($index, &$dispatch) {
$dispatch($index + 1);
});
};
$dispatch(0);
}
}
// 使用
$app = new MiddlewareChain();
$app->use(function ($request, $next) {
echo "1. Before" . PHP_EOL;
$next();
echo "1. After" . PHP_EOL;
});
$app->use(function ($request, $next) {
echo "2. Before" . PHP_EOL;
$next();
echo "2. After" . PHP_EOL;
});
$app->use(function ($request, $next) {
echo "3. Process" . PHP_EOL;
$request['result'] = "Done";
});
$app->execute([]);
// 1. Before
// 2. Before
// 3. Process
// 2. After
// 1. After三、适用场景
| 场景 | 说明 |
|---|---|
| 审批流程 | 多级审批 |
| 中间件 | Express、Koa、Redux |
| 事件冒泡 | DOM 事件传播 |
| 日志处理 | 不同级别日志 |
| 异常处理 | 多层 try-catch |
| 过滤器 | 数据校验、过滤 |
| 路由匹配 | 多路由规则 |
四、优缺点分析
| 优点 | 缺点 |
|---|---|
| 解耦发送者和处理者 | 请求可能无人处理 |
| 灵活调整链结构 | 调试困难 |
| 符合单一职责 | 性能开销(链遍历) |
| 符合开闭原则 | 可能形成循环链 |
| 支持动态组合 | 链过长影响性能 |
五、常见踩坑与问题排查
5.1 循环链导致死循环
php
<?php
// 问题:A → B → A 形成循环
$a = new ConcreteHandlerA();
$b = new ConcreteHandlerB();
$a->setNext($b);
$b->setNext($a); // 危险!
// 解决:构建链时检查,避免循环
function validateChain(Handler $head): bool
{
$visited = [];
$current = $head;
while ($current !== null) {
if (in_array($current, $visited, true)) return false; // 检测到循环
$visited[] = $current;
$current = $current->next ?? null;
}
return true;
}5.2 请求未被处理
php
<?php
// 问题:链末端无人处理,请求丢失
$chain->handle("unknown"); // 返回 null
// 解决:在链末尾添加默认处理者
class DefaultHandler extends Handler
{
public function handle(string $request): string
{
return "Default: {$request} not handled";
}
}
// 构建链时添加默认处理
$chain->setNext(new DefaultHandler());5.3 忘记调用 next
php
<?php
// 问题:中间件忘记调用 $next()
$app->use(function ($ctx, $next) {
echo "Before" . PHP_EOL;
$next; // 忘记调用,后续中间件不执行
echo "After" . PHP_EOL;
});
// 解决:始终调用 $next()
$app->use(function ($ctx, $next) {
echo "Before" . PHP_EOL;
$next();
echo "After" . PHP_EOL;
});六、优化方案与进阶
6.1 函数式责任链
php
<?php
function createChain(array $handlers): callable
{
return function ($request, callable $next) use ($handlers) {
if (empty($handlers)) return $next();
$first = array_shift($handlers);
return $first($request, function () use ($handlers, $request, $next) {
return createChain($handlers)($request, $next);
});
};
}
// 使用
$chain = createChain([
function ($req, $next) { return $req > 0 ? $req * 2 : $next(); },
function ($req, $next) { return $req > 10 ? $req + 1 : $next(); },
function ($req, $next) { return $next(); },
]);
echo $chain(5, fn() => -1) . PHP_EOL; // 10
echo $chain(15, fn() => -1) . PHP_EOL; // 16
echo $chain(-5, fn() => -1) . PHP_EOL; // -16.2 异步责任链
php
<?php
abstract class AsyncHandler
{
protected ?AsyncHandler $next = null;
public function setNext(AsyncHandler $handler): AsyncHandler
{
$this->next = $handler;
return $handler;
}
public function handle($request): mixed
{
$result = $this->process($request);
if ($result !== null) return $result;
if ($this->next !== null) return $this->next->handle($request);
return null;
}
abstract protected function process($request): mixed;
}七、全文总结
责任链模式的核心是 将请求沿处理者链传递,每个处理者决定处理或转发。
核心要点:
- 处理者持有下一个处理者引用,形成链
- 支持链式构建,灵活调整顺序
- 是中间件、审批流程、事件冒泡的核心
- 注意避免循环链、请求丢失、忘记调用 next
- 函数式实现可简化责任链构建
