Skip to content

责任链模式(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 Charlie

2.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; // -1

6.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;
}

七、全文总结

责任链模式的核心是 将请求沿处理者链传递,每个处理者决定处理或转发

核心要点

  1. 处理者持有下一个处理者引用,形成链
  2. 支持链式构建,灵活调整顺序
  3. 是中间件、审批流程、事件冒泡的核心
  4. 注意避免循环链、请求丢失、忘记调用 next
  5. 函数式实现可简化责任链构建