纯责任链和不纯责任链有什么区别?
简化版
纯责任链要求一个请求最终只能被一个处理器处理,处理后链路结束;不纯责任链允许多个处理器共同处理同一个请求,也允许某些节点只做增强后继续传递。真实项目里更常见的是不纯责任链,比如过滤器链、拦截器链和网关链路。
详细版
纯责任链更接近经典定义:请求沿链传递,直到某个处理器能处理它,处理完成后不再向后传递。如果链上没有处理器能处理,可能走默认逻辑或返回失败。
不纯责任链更接近工程实践:一个请求可能被多个处理器依次处理。比如一次 HTTP 请求进入系统后,日志、鉴权、限流、参数校验、业务处理都可能执行,每个节点都承担一部分职责。
区别可以从三点看:
- 处理者数量:纯责任链通常一个处理者处理;不纯责任链可能多个处理者处理;
- 传递方式:纯责任链命中后停止;不纯责任链处理后可能继续;
- 使用场景:纯责任链适合审批、路由分发;不纯责任链适合过滤器、校验链、增强链。
面试时可以补充:设计模式不是死模板,真实框架中大量责任链都是不纯责任链,但仍然符合“请求沿处理器链传递、发送者与处理者解耦”的核心思想。
完整版教学
一、纯责任链强调“谁能处理就交给谁”
纯责任链的典型语义是:一个请求只需要一个处理者完成。
比如请假审批:
- 1 天以内主管审批;
- 3 天以内经理审批;
- 7 天以内总监审批。
伪代码可以这样写:
class ManagerHandler extends Handler {
void handle(LeaveRequest request) {
if (request.getDays() <= 3) {
approve(request);
return;
}
next.handle(request);
}
}
一旦经理能处理,就不会再交给总监。
这种链路像“找负责人”:请求不是让每个人都处理,而是沿着链寻找合适的人。
二、不纯责任链强调“多个节点分段处理”
不纯责任链中,多个处理器都可能处理同一个请求。
比如 Web 请求:
请求 -> 日志过滤器 -> 鉴权过滤器 -> 限流过滤器 -> 业务处理器 -> 响应
日志过滤器不会说“我记录完日志,所以业务不用执行了”。它通常会处理一部分逻辑,然后继续放行。
示意代码:
class LogFilter implements Filter {
void doFilter(Request request, FilterChain chain) {
long start = System.currentTimeMillis();
chain.doFilter(request);
long cost = System.currentTimeMillis() - start;
log.info("cost={}", cost);
}
}
这里日志过滤器既处理了请求,又让请求继续向后传递。
三、两者的核心差异在“处理后是否继续”
纯责任链:
A 不能处理 -> B 不能处理 -> C 能处理 -> 停止
不纯责任链:
A 处理并继续 -> B 处理并继续 -> C 处理并继续 -> 结束
当然,不纯责任链也可以短路。比如鉴权失败后直接返回 401,不再进入后面的限流和业务处理器。
因此不纯责任链更灵活:它既支持增强,也支持拦截。
四、为什么真实项目更常见不纯责任链
真实系统中,一个请求往往不是由某一个节点单独完成,而是需要多个横切能力协同:
- 日志记录;
- 身份认证;
- 权限判断;
- 限流;
- 参数校验;
- 灰度规则;
- 业务执行;
- 响应包装。
这些能力天然适合拆成多个节点逐步处理,所以不纯责任链在 Web 框架、RPC 框架、网关、消息消费、风控系统里特别常见。
五、纯责任链更适合“路由型处理”
纯责任链更适合下面这些问题:
- 谁来审批;
- 谁来解析这种格式;
- 谁来处理这种命令;
- 谁来兜底这种异常;
- 哪个路由规则命中请求。
这些问题的特征是:请求最终通常只需要一个处理者。
如果多个处理器都执行,反而可能重复处理或造成副作用。
六、答题时不要把“不纯”理解成“不规范”
“不纯责任链”里的“不纯”不是贬义。它只是说它不完全符合经典定义中“只由一个处理者处理”的限制。
很多成熟框架都使用不纯责任链思想。比如过滤器链、拦截器链、插件链,本质上都允许多个节点处理同一请求。
面试时可以这样表达:经典责任链偏请求分派,工程责任链偏流程增强,两者都围绕链式传递和解耦处理者展开。
七、命中一个与逐段处理的差异
纯链 5 个处理器中通常只有 1 个最终处理;不纯链可能让 5 个节点全部增强同一请求,执行次数从 1 变为 5。
pure: pass... -> one handles; impure: H1 -> H2 -> H3 all may process
责任链是否正确,不能只看节点都被注册,还要看请求到底经过哪些节点、在哪里停止以及返回阶段如何展开。应把“继续、已处理、拒绝、系统失败”建成可区分的结果,并让日志能够还原一次请求的完整路径。
八、链路顺序、短路与可观测性
| 检查维度 | 应确认的内容 |
|---|---|
| 机制正确性 | 回答时应落到处理后是否继续、结果如何合并,而不是纠结名称。 |
| 适用边界 | “纯/不纯”是工程分类,不是价值判断;真实过滤器链常允许多个节点处理并继续。 |
| 测试证据 | 对同一请求分别验证首节点命中、中间节点命中和走到链尾,断言实际执行节点集合 |
| 工程代价 | 重点测量链长、各节点耗时、最坏路径延迟和短路率,并确认动态装配不会产生重复节点或排序漂移 |
易错点:回答时应落到处理后是否继续、结果如何合并,而不是纠结名称。
九、常见误区与追问
- 误区:不纯责任链是不规范的实现。 它只是允许多个节点分段处理,Servlet Filter 等工程实践正是常见例子。
- 误区:责任链一定会让每个处理器都执行。 纯链可能命中一个节点就结束,不纯链也可能因拒绝、异常或短路提前终止,必须由链契约定义。
- 追问:纯链如何避免多个节点都声称能处理? 定义匹配优先级和首个命中即停止,并用测试覆盖条件重叠。
- 追问:责任链如何避免请求到链尾无人处理? 提供明确的默认处理器或链尾失败结果,并对未处理请求记录指标,不能静默成功。
- 追问:责任链上线后最少要记录哪些信息? 至少记录链版本、节点顺序、逐节点耗时与决策、短路位置、异常和最终处理结果。
十、加强记忆
纯责任链记成“找到一个能处理的人就停”,不纯责任链记成“多个节点可以依次处理,也可以中途拦截”。审批、路由、命令分派更偏纯责任链;过滤器、拦截器、网关、校验链更偏不纯责任链。真实项目里不纯责任链更常见。