设计模式 · 责任链模式(Chain of Responsibility Pattern)
设计模式 · 责任链模式(Chain of Responsibility Pattern)
前言
在业务系统中,经常会遇到“多级审批”“多层校验”“多步骤过滤”这类流程。
比如项目上线审批:
- 平时只需要三级负责人审批;
- 618 大促期间,需要额外经过二级负责人审批;
- 618 核心时间段,还需要一级负责人审批;
- 每一级审批通过后,才可以继续向下流转。
如果直接用 if-else 写,代码很快就会变成一大坨判断逻辑。新增审批级别、调整审批顺序、修改审批规则,都需要改原来的核心方法。
责任链模式的作用,就是把这些审批环节拆成一个个独立节点,再用 next 把它们串成一条链。
本文代码链接:https://github.com/likerhood/CodeDesignWork/tree/main/codedesign12.0-0
一、核心定义
责任链模式(Chain of Responsibility Pattern) 是一种行为型设计模式,将请求的发送者和接收者解耦,让多个对象都有机会处理请求,将这些对象串成一条链,请求沿链传递,直到有对象处理它为止。
它的核心思想是:
将多个处理对象串成一条链,请求从链头开始传递,每个节点决定自己是否处理,以及是否继续传给下一个节点。
- 解决问题:多级审批、多层过滤、多步骤验证等”流程链”场景中,if-else 造成的强耦合与难扩展
- 关键特征:
- 每个节点实现同一抽象类/接口,决定自己处理还是传给下一个
- 链的组装在运行时进行,可动态调整
- 调用方只持有链头引用,不感知链的内部结构
二、标准体系结构图
classDiagram
class Handler {
<<abstract>>
-next: Handler
+setNext(handler: Handler) Handler
+handle(request: Request) Response
}
class ConcreteHandler1 {
+handle(request: Request) Response
}
class ConcreteHandler2 {
+handle(request: Request) Response
}
class ConcreteHandler3 {
+handle(request: Request) Response
}
class Client {
}
Handler <|-- ConcreteHandler1 : 继承
Handler <|-- ConcreteHandler2 : 继承
Handler <|-- ConcreteHandler3 : 继承
Handler o--> Handler : next(链接下一个)
Client --> Handler : 只持有链头引用
| 角色 | 作用 | 报销审批中的对应对象 |
|---|---|---|
抽象处理者 Handler |
定义处理方法,并持有下一个节点 | 抽象审批人 |
具体处理者 ConcreteHandler |
判断自己是否能处理请求 | 组长、经理、总监、CFO |
客户端 Client |
组装链条,并发起请求 | 报销服务或系统启动配置 |
三、场景推演
假设公司规定如下:
| 请假天数 | 审批人 |
|---|---|
| 1 天以内 | 组长 |
| 3 天以内 | 经理 |
| 超过 3 天 | 总监 |
员工提交请假申请后,不需要自己判断应该找组长、经理还是总监。
员工只需要提交申请:
1 | 员工提交请假申请 → 组长 → 经理 → 总监 |
每个审批人收到申请后,只判断一件事:
当前请假天数是否在我的审批范围内?
- 如果在自己的审批范围内,就审批通过;
- 如果超出自己的权限,就交给下一个审批人。
代码:
1 | public class LeaveApprovalChainDemo { |
四、实战案例
4.1 需求分析
618 大促审批流程升级规则:
1 | 任何时期: |
面条代码的问题:
1 | // if 嵌套地狱,增加一级审批 = 新增一坨 if |
问题:
- 所有审批逻辑堆在一个方法,职责混乱
- 新增审批级别 → 修改原有 if 逻辑,违反开闭原则
- 不同时期的审批规则变化 → 难以维护,改动容易引入 bug
责任链模式的解法:
1 | 链组装(只做一次): |
4.2 架构图
4.2.1 面条代码架构图

4.2.2 责任链模式架构图

4.3 类图对比
4.3.1 面条代码类图
classDiagram
class AuthController {
-f: SimpleDateFormat
+doAuth(uId: String, orderId: String, authDate: Date) AuthInfo
}
class AuthService {
<<static 审批记录>>
+queryAuthInfo(uId: String, orderId: String) Date$
+auth(uId: String, orderId: String) void$
}
class AuthInfo {
-code: String
-info: String
}
AuthController ..> AuthService : 多次硬编码调用
AuthController ..> AuthInfo : 创建并返回
note for AuthController "三级审批逻辑全部堆在\ndoAuth() 一个方法中\n含日期判断 if-else"
4.3.2 责任链模式类图
classDiagram
class AuthLink {
<<abstract 抽象链节点>>
#levelUserId: String
#levelUserName: String
#next: AuthLink
+getNext() AuthLink
+appendNext(next: AuthLink) AuthLink
+doAuth(uId: String, orderId: String, authDate: Date) AuthInfo*
}
class Level3AuthLink {
<<三级负责人(王工)>>
+doAuth(...) AuthInfo
}
class Level2AuthLink {
<<二级负责人(张经理)>>
-beginDate: Date
-endDate: Date
+doAuth(...) AuthInfo
}
class Level1AuthLink {
<<一级负责人(段总)>>
-beginDate: Date
-endDate: Date
+doAuth(...) AuthInfo
}
class AuthService {
-authMap: Map~String, Date~$
+queryAuthInfo(uId: String, orderId: String) Date$
+auth(uId: String, orderId: String) void$
}
class AuthInfo {
-code: String
-info: String
}
AuthLink <|-- Level3AuthLink
AuthLink <|-- Level2AuthLink
AuthLink <|-- Level1AuthLink
AuthLink o--> AuthLink : next(链接下一节点)
Level3AuthLink ..> AuthService
Level2AuthLink ..> AuthService
Level1AuthLink ..> AuthService
Level3AuthLink ..> AuthInfo
Level2AuthLink ..> AuthInfo
Level1AuthLink ..> AuthInfo
note for AuthLink "核心:持有 next 引用\nappendNext() 构建链\ndoAuth() 抽象方法由子类实现"
4.4 时序图
4.4.1 面条代码时序图
sequenceDiagram
participant Client as ApiTest
participant AC as AuthController
participant AS as AuthService
Client ->> AC: doAuth("小傅哥", "1000998...", date)
AC ->> AS: queryAuthInfo("1000013", orderId)
AS -->> AC: null
AC -->> Client: 待三级审批(王工)
Client ->> AS: auth("1000013", orderId) [王工审批]
Client ->> AC: doAuth(...)
AC ->> AS: queryAuthInfo("1000013", orderId) → 有时间
AC ->> AS: queryAuthInfo("1000012", orderId)
AS -->> AC: null
AC -->> Client: 待二级审批(张经理)
Client ->> AS: auth("1000012", orderId) [张经理审批]
Client ->> AC: doAuth(...)
AC ->> AS: queryAuthInfo("1000013") → 有
AC ->> AS: queryAuthInfo("1000012") → 有
AC ->> AS: queryAuthInfo("1000011", orderId)
AS -->> AC: null
AC -->> Client: 待一级审批(段总)
Client ->> AS: auth("1000011", orderId) [段总审批]
Client ->> AC: doAuth(...)
AC ->> AS: 三次查询全部有时间记录
AC -->> Client: 审批完成
4.4.2 责任链模式时序图
sequenceDiagram
participant Client as ApiTest
participant L3 as Level3AuthLink(王工)
participant L2 as Level2AuthLink(张经理)
participant L1 as Level1AuthLink(段总)
participant AS as AuthService
Note over Client: 组装链头:L3→L2→L1
Client ->> L3: authLink.doAuth("小傅哥", orderId, date)
L3 ->> AS: queryAuthInfo("1000013", orderId) → null
L3 -->> Client: 0001:待三级审批(王工)
Client ->> AS: auth("1000013", orderId)
Client ->> L3: authLink.doAuth(...)
L3 ->> AS: queryAuthInfo("1000013") → 有记录
Note over L3: next != null,传递
L3 ->> L2: next.doAuth(...)
L2 ->> AS: queryAuthInfo("1000012", orderId) → null
L2 -->> Client: 0001:待二级审批(张经理)
Client ->> AS: auth("1000012", orderId)
Client ->> L3: authLink.doAuth(...)
L3 ->> AS: queryAuthInfo("1000013") → 有
L3 ->> L2: next.doAuth(...)
L2 ->> AS: queryAuthInfo("1000012") → 有
Note over L2: 时间在 6.1~6.25,传递给 L1
L2 ->> L1: next.doAuth(...)
L1 ->> AS: queryAuthInfo("1000011", orderId) → null
L1 -->> Client: 0001:待一级审批(段总)
Client ->> AS: auth("1000011", orderId)
Client ->> L3: authLink.doAuth(...)
L3 ->> L2: 传递
L2 ->> L1: 传递
L1 ->> AS: queryAuthInfo("1000011") → 有记录
Note over L1: next == null
L1 -->> Client: 0000:审批完成(段总,时间:...)
4.5 代码分析
4.5.1 抽象链节点 AuthLink(责任链核心骨架)
1 | // AuthLink.java |
4.5.2 三级节点 Level3AuthLink(链头,无时间限制,总是生效)
1 | public class Level3AuthLink extends AuthLink { |
4.5.3 二级节点 Level2AuthLink(仅 6.1~6.25 期间生效)
1 | public class Level2AuthLink extends AuthLink { |
4.5.4 责任链组装与测试
1 | // ApiTest.java |
总结
| 维度 | 面条代码 | 责任链模式 |
|---|---|---|
| 审批逻辑位置 | 全部堆在 doAuth() 一个方法 |
每级封装在各自节点类中 |
| 新增审批级别 | 修改原有 if 逻辑,影响稳定性 | 新建节点类,appendNext() 插入链中 |
| 审批顺序调整 | 重写 if 逻辑 | 修改链的组装顺序即可 |
| 时间判断逻辑 | 散落在 if 条件中 | 封装在对应节点内部 |
| 符合开闭原则 | 否(修改原有代码) | 是(扩展新增,不改旧代码) |
责任链模式的关键实现要点:
appendNext()返回this:支持链式调用,是优雅组装责任链的语法糖。- 每个节点的三判断模式:① 当前节点是否满足处理条件?不满足 → 提前返回。② 有无下一节点?无 → 返回最终结果。③ 传递给下一节点
next.doAuth()。- 链的可配置化:本案例是代码硬组装,生产中常见的方式是从数据库/配置文件读取审批链配置,动态组装,实现完全可配置的流程引擎。
责任链模式的适用场景:
| 场景 | 说明 |
|---|---|
| 多级审批流程 | 报销审批、上线审批、权限申请等 |
| HTTP 过滤器/拦截器链 | javax.servlet.Filter 就是责任链 |
| Spring Security 过滤器链 | 多个 SecurityFilter 串成链 |
| Netty ChannelPipeline | 入站/出站处理器链 |
| 日志级别过滤 | 多个 Logger 节点判断日志级别 |
与其他模式对比:
- 责任链模式 vs 装饰器模式:装饰器的每个节点都会执行(叠加功能),责任链的节点可以中断链路(提前返回)
- 责任链模式 vs 组合模式:组合模式是树形结构,可双向遍历;责任链是线性结构,单向传递
- 责任链模式 vs 策略模式:策略在一组算法中选择一个执行,责任链是顺序依次流过各节点
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Liker's Blog!
评论