文章目录
-
- 前言
- 一、核心定义
- 二、标准体系结构图
- 三、场景推演
- 四、实战案例
-
- [4.1 需求分析](#4.1 需求分析)
- [4.2 架构图](#4.2 架构图)
-
- [4.2.1 面条代码架构图](#4.2.1 面条代码架构图)
- [4.2.2 责任链模式架构图](#4.2.2 责任链模式架构图)
- [4.3 类图对比](#4.3 类图对比)
-
- [4.3.1 面条代码类图](#4.3.1 面条代码类图)
- [4.3.2 责任链模式类图](#4.3.2 责任链模式类图)
- [4.4 时序图](#4.4 时序图)
-
- [4.4.1 面条代码时序图](#4.4.1 面条代码时序图)
- [4.4.2 责任链模式时序图](#4.4.2 责任链模式时序图)
- [4.5 代码分析](#4.5 代码分析)
-
- [4.5.1 抽象链节点 `AuthLink`(责任链核心骨架)](#4.5.1 抽象链节点
AuthLink(责任链核心骨架)) - [4.5.2 三级节点 `Level3AuthLink`(链头,无时间限制,总是生效)](#4.5.2 三级节点
Level3AuthLink(链头,无时间限制,总是生效)) - [4.5.3 二级节点 `Level2AuthLink`(仅 6.1~6.25 期间生效)](#4.5.3 二级节点
Level2AuthLink(仅 6.1~6.25 期间生效)) - [4.5.4 责任链组装与测试](#4.5.4 责任链组装与测试)
- [4.5.1 抽象链节点 `AuthLink`(责任链核心骨架)](#4.5.1 抽象链节点
- 总结
前言
在业务系统中,经常会遇到"多级审批""多层校验""多步骤过滤"这类流程。
比如项目上线审批:
- 平时只需要三级负责人审批;
- 618 大促期间,需要额外经过二级负责人审批;
- 618 核心时间段,还需要一级负责人审批;
- 每一级审批通过后,才可以继续向下流转。
如果直接用 if-else 写,代码很快就会变成一大坨判断逻辑。新增审批级别、调整审批顺序、修改审批规则,都需要改原来的核心方法。
责任链模式的作用,就是把这些审批环节拆成一个个独立节点,再用 next 把它们串成一条链。
本文代码链接:https://github.com/likerhood/CodeDesignWork/tree/main/codedesign12.0-0
一、核心定义
责任链模式(Chain of Responsibility Pattern) 是一种行为型设计模式,将请求的发送者和接收者解耦,让多个对象都有机会处理请求,将这些对象串成一条链,请求沿链传递,直到有对象处理它为止。
它的核心思想是:
将多个处理对象串成一条链,请求从链头开始传递,每个节点决定自己是否处理,以及是否继续传给下一个节点。
- 解决问题:多级审批、多层过滤、多步骤验证等"流程链"场景中,if-else 造成的强耦合与难扩展
- 关键特征 :
- 每个节点实现同一抽象类/接口,决定自己处理还是传给下一个
- 链的组装在运行时进行,可动态调整
- 调用方只持有链头引用,不感知链的内部结构
二、标准体系结构图
继承
继承
继承
next(链接下一个)
只持有链头引用
<<abstract>>
Handler
-next: Handler
+setNext(handler: Handler) : Handler
+handle(request: Request) : Response
ConcreteHandler1
+handle(request: Request) : Response
ConcreteHandler2
+handle(request: Request) : Response
ConcreteHandler3
+handle(request: Request) : Response
Client
| 角色 | 作用 | 报销审批中的对应对象 |
|---|---|---|
抽象处理者 Handler |
定义处理方法,并持有下一个节点 | 抽象审批人 |
具体处理者 ConcreteHandler |
判断自己是否能处理请求 | 组长、经理、总监、CFO |
客户端 Client |
组装链条,并发起请求 | 报销服务或系统启动配置 |
三、场景推演
假设公司规定如下:
| 请假天数 | 审批人 |
|---|---|
| 1 天以内 | 组长 |
| 3 天以内 | 经理 |
| 超过 3 天 | 总监 |
员工提交请假申请后,不需要自己判断应该找组长、经理还是总监。
员工只需要提交申请:
员工提交请假申请 → 组长 → 经理 → 总监
每个审批人收到申请后,只判断一件事:
当前请假天数是否在我的审批范围内?
- 如果在自己的审批范围内,就审批通过;
- 如果超出自己的权限,就交给下一个审批人。
代码:
java
public class LeaveApprovalChainDemo {
/**
* 请求对象:员工提交的请假申请。
*/
static class LeaveRequest {
private final String employeeName;
private final int days;
private final String reason;
public LeaveRequest(String employeeName, int days, String reason) {
if (days <= 0) {
throw new IllegalArgumentException("请假天数必须大于 0");
}
this.employeeName = employeeName;
this.days = days;
this.reason = reason;
}
public String getEmployeeName() {
return employeeName;
}
public int getDays() {
return days;
}
public String getReason() {
return reason;
}
}
/**
* 抽象处理者:审批人。
*
* 每个审批人都保存下一个审批人。
* 当前审批人能够处理,就直接审批;
* 当前审批人不能处理,就将申请交给下一个审批人。
*/
static abstract class Approver {
private Approver next;
/**
* 设置下一个审批人。
*/
public Approver setNext(Approver next) {
this.next = next;
return next;
}
/**
* 处理请假申请。
*/
public final void handle(LeaveRequest request) {
System.out.printf(
"%s查看申请:%s请假%d天,原因:%s%n",
getRole(),
request.getEmployeeName(),
request.getDays(),
request.getReason()
);
if (canApprove(request)) {
System.out.printf(
"%s审批通过:%s请假%d天%n",
getRole(),
request.getEmployeeName(),
request.getDays()
);
return;
}
if (next != null) {
System.out.printf(
"%s权限不足,转交给%s%n",
getRole(),
next.getRole()
);
next.handle(request);
} else {
System.out.println("当前申请没有审批人可以处理");
}
}
/**
* 判断当前审批人能否处理这张请假申请。
*/
protected abstract boolean canApprove(LeaveRequest request);
/**
* 获取当前审批人的角色名称。
*/
protected abstract String getRole();
}
/**
* 具体处理者一:组长。
*
* 组长最多审批 1 天请假。
*/
static class TeamLeader extends Approver {
@Override
protected boolean canApprove(LeaveRequest request) {
return request.getDays() <= 1;
}
@Override
protected String getRole() {
return "组长";
}
}
/**
* 具体处理者二:经理。
*
* 经理最多审批 3 天请假。
*/
static class Manager extends Approver {
@Override
protected boolean canApprove(LeaveRequest request) {
return request.getDays() <= 3;
}
@Override
protected String getRole() {
return "经理";
}
}
/**
* 具体处理者三:总监。
*
* 总监作为链条最后一个节点,处理超过 3 天的请假。
*/
static class Director extends Approver {
@Override
protected boolean canApprove(LeaveRequest request) {
return true;
}
@Override
protected String getRole() {
return "总监";
}
}
public static void main(String[] args) {
/*
* 组装责任链:
* 组长 -> 经理 -> 总监
*/
Approver chain = new TeamLeader();
chain.setNext(new Manager())
.setNext(new Director());
LeaveRequest request1 = new LeaveRequest(
"小王",
1,
"家中有事"
);
LeaveRequest request2 = new LeaveRequest(
"小李",
2,
"外出办事"
);
LeaveRequest request3 = new LeaveRequest(
"小张",
5,
"回家探亲"
);
System.out.println("===== 第一张请假申请 =====");
chain.handle(request1);
System.out.println("\n===== 第二张请假申请 =====");
chain.handle(request2);
System.out.println("\n===== 第三张请假申请 =====");
chain.handle(request3);
}
}
四、实战案例
4.1 需求分析
618 大促审批流程升级规则:
任何时期:
└── 三级审批(王工 1000013)必须通过
2020-06-01 ~ 2020-06-25 期间,额外加:
└── 二级审批(张经理 1000012)必须通过
2020-06-11 ~ 2020-06-20 期间,再额外加:
└── 一级审批(段总 1000011)必须通过
面条代码的问题:
java
// if 嵌套地狱,增加一级审批 = 新增一坨 if
Date date = AuthService.queryAuthInfo("1000013", orderId);
if (null == date) return ... 王工;
if (日期 in 6.1~6.25) {
date = AuthService.queryAuthInfo("1000012", orderId);
if (null == date) return ... 张经理;
}
if (日期 in 6.11~6.20) {
date = AuthService.queryAuthInfo("1000011", orderId);
if (null == date) return ... 段总;
}
return 审批完成;
问题:
- 所有审批逻辑堆在一个方法,职责混乱
- 新增审批级别 → 修改原有 if 逻辑,违反开闭原则
- 不同时期的审批规则变化 → 难以维护,改动容易引入 bug
责任链模式的解法:
链组装(只做一次):
Level3AuthLink(王工)→ Level2AuthLink(张经理)→ Level1AuthLink(段总)
调用方:
authLink.doAuth(uId, orderId, authDate)
└── Level3AuthLink.doAuth() 检查三级是否通过?
├── 未通过 → 返回"待三级审批"
└── 通过 → 检查时间是否在 6.1~6.25?
├── 否 → 返回完成(不需要二级审批)
└── 是 → next.doAuth()(传给 Level2)
└── Level2AuthLink.doAuth() ...
4.2 架构图
4.2.1 面条代码架构图

4.2.2 责任链模式架构图

4.3 类图对比
4.3.1 面条代码类图
多次硬编码调用
创建并返回
AuthController
-f: SimpleDateFormat
+doAuth(uId: String, orderId: String, authDate: Date) : AuthInfo
<<static 审批记录>>
AuthService
+queryAuthInfo(uId: String, orderId: String) : Date
+auth(uId: String, orderId: String) : void
AuthInfo
-code: String
-info: String
三级审批逻辑全部堆在\ndoAuth() 一个方法中\n含日期判断 if-else
4.3.2 责任链模式类图
next(链接下一节点)
<<abstract 抽象链节点>>
AuthLink
#levelUserId: String
#levelUserName: String
#next: AuthLink
+getNext() : AuthLink
+appendNext(next: AuthLink) : AuthLink
+doAuth(uId: String, orderId: String, authDate: Date) : AuthInfo
<<三级负责人(王工)>>
Level3AuthLink
+doAuth(...) : AuthInfo
<<二级负责人(张经理)>>
Level2AuthLink
-beginDate: Date
-endDate: Date
+doAuth(...) : AuthInfo
<<一级负责人(段总)>>
Level1AuthLink
-beginDate: Date
-endDate: Date
+doAuth(...) : AuthInfo
AuthService
-authMap: Map<String, Date>
+queryAuthInfo(uId: String, orderId: String) : Date
+auth(uId: String, orderId: String) : void
AuthInfo
-code: String
-info: String
核心:持有 next 引用\nappendNext() 构建链\ndoAuth() 抽象方法由子类实现
4.4 时序图
4.4.1 面条代码时序图
AuthService AuthController ApiTest AuthService AuthController ApiTest doAuth("小傅哥", "1000998...", date) queryAuthInfo("1000013", orderId) null 待三级审批(王工) auth("1000013", orderId) [王工审批] doAuth(...) queryAuthInfo("1000013", orderId) → 有时间 queryAuthInfo("1000012", orderId) null 待二级审批(张经理) auth("1000012", orderId) [张经理审批] doAuth(...) queryAuthInfo("1000013") → 有 queryAuthInfo("1000012") → 有 queryAuthInfo("1000011", orderId) null 待一级审批(段总) auth("1000011", orderId) [段总审批] doAuth(...) 三次查询全部有时间记录 审批完成
4.4.2 责任链模式时序图
AuthService Level1AuthLink(段总) Level2AuthLink(张经理) Level3AuthLink(王工) ApiTest AuthService Level1AuthLink(段总) Level2AuthLink(张经理) Level3AuthLink(王工) ApiTest 组装链头:L3→L2→L1 next != null,传递 时间在 6.1~6.25,传递给 L1 next == null authLink.doAuth("小傅哥", orderId, date) queryAuthInfo("1000013", orderId) → null 0001:待三级审批(王工) auth("1000013", orderId) authLink.doAuth(...) queryAuthInfo("1000013") → 有记录 next.doAuth(...) queryAuthInfo("1000012", orderId) → null 0001:待二级审批(张经理) auth("1000012", orderId) authLink.doAuth(...) queryAuthInfo("1000013") → 有 next.doAuth(...) queryAuthInfo("1000012") → 有 next.doAuth(...) queryAuthInfo("1000011", orderId) → null 0001:待一级审批(段总) auth("1000011", orderId) authLink.doAuth(...) 传递 传递 queryAuthInfo("1000011") → 有记录 0000:审批完成(段总,时间:...)
4.5 代码分析
4.5.1 抽象链节点 AuthLink(责任链核心骨架)
java
// AuthLink.java
public abstract class AuthLink {
protected String levelUserId; // 当前节点的审批人ID
protected String levelUserName; // 当前节点的审批人姓名
protected AuthLink next; // 下一个节点(链接)
public AuthLink(String levelUserId, String levelUserName) {
this.levelUserId = levelUserId;
this.levelUserName = levelUserName;
}
public AuthLink getNext() { return next; }
// ✅ 核心:appendNext() 支持链式调用组装责任链
// new Level3().appendNext(new Level2().appendNext(new Level1()))
public AuthLink appendNext(AuthLink next) {
this.next = next;
return this; // 返回 this,支持链式调用
}
// 抽象方法:每个具体节点实现自己的审批逻辑
public abstract AuthInfo doAuth(String uId, String orderId, Date authDate);
}
4.5.2 三级节点 Level3AuthLink(链头,无时间限制,总是生效)
java
public class Level3AuthLink extends AuthLink {
public Level3AuthLink(String levelUserId, String levelUserName) {
super(levelUserId, levelUserName);
}
public AuthInfo doAuth(String uId, String orderId, Date authDate) {
// ① 查询三级审批是否已完成
Date date = AuthService.queryAuthInfo(levelUserId, orderId);
if (null == date) {
// ② 未审批 → 返回"待审批",请求在此节点终止
return new AuthInfo("0001", "单号:", orderId, " 状态:待三级审批负责人 ", levelUserName);
}
AuthLink next = super.getNext();
if (null == next) {
// ③ 已审批 + 无下一节点 → 链路结束,返回完成
return new AuthInfo("0000", "单号:", orderId, " 状态:一级审批完成负责人",
" 时间:", f.format(date), " 审批人:", levelUserName);
}
// ④ 已审批 + 有下一节点 → 传递给下一节点继续处理
return next.doAuth(uId, orderId, authDate);
}
}
4.5.3 二级节点 Level2AuthLink(仅 6.1~6.25 期间生效)
java
public class Level2AuthLink extends AuthLink {
// ✅ 关键:时间判断封装在节点内部,不暴露给外部
private Date beginDate = f.parse("2020-06-01 00:00:00");
private Date endDate = f.parse("2020-06-25 23:59:59");
public AuthInfo doAuth(String uId, String orderId, Date authDate) {
Date date = AuthService.queryAuthInfo(levelUserId, orderId);
if (null == date) {
return new AuthInfo("0001", "单号:", orderId, " 状态:待二级审批负责人 ", levelUserName);
}
AuthLink next = super.getNext();
if (null == next) {
return new AuthInfo("0000", "单号:", orderId, " 状态:二级审批完成负责人",
" 时间:", f.format(date), " 审批人:", levelUserName);
}
// ✅ 不在大促时间段 → 不需要再往上审批,直接返回完成
if (authDate.before(beginDate) || authDate.after(endDate)) {
return new AuthInfo("0000", "单号:", orderId, " 状态:二级审批完成负责人",
" 时间:", f.format(date), " 审批人:", levelUserName);
}
return next.doAuth(uId, orderId, authDate);
}
}
4.5.4 责任链组装与测试
java
// ApiTest.java
@Test
public void test_AuthLink() throws ParseException {
// ✅ 责任链组装:Level3(王工)→ Level2(张经理)→ Level1(段总)
AuthLink authLink = new Level3AuthLink("1000013", "王工")
.appendNext(new Level2AuthLink("1000012", "张经理")
.appendNext(new Level1AuthLink("1000011", "段总")));
// 查询当前状态(三级未审批)
logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("小傅哥", "1000998004813441", new Date())));
// 模拟三级审批
AuthService.auth("1000013", "1000998004813441");
logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("小傅哥", "1000998004813441", new Date())));
// 模拟二级审批
AuthService.auth("1000012", "1000998004813441");
logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("小傅哥", "1000998004813441", new Date())));
// 模拟一级审批
AuthService.auth("1000011", "1000998004813441");
logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("小傅哥", "1000998004813441", new Date())));
}
// 输出:
// {"code":"0001","info":"单号:1000998... 状态:待三级审批负责人 王工"}
// {"code":"0001","info":"单号:1000998... 状态:待二级审批负责人 张经理"}
// {"code":"0001","info":"单号:1000998... 状态:待一级审批负责人 段总"}
// {"code":"0000","info":"单号:1000998... 状态:一级审批完成负责人 时间:... 审批人:段总"}
总结
| 维度 | 面条代码 | 责任链模式 |
|---|---|---|
| 审批逻辑位置 | 全部堆在 doAuth() 一个方法 |
每级封装在各自节点类中 |
| 新增审批级别 | 修改原有 if 逻辑,影响稳定性 | 新建节点类,appendNext() 插入链中 |
| 审批顺序调整 | 重写 if 逻辑 | 修改链的组装顺序即可 |
| 时间判断逻辑 | 散落在 if 条件中 | 封装在对应节点内部 |
| 符合开闭原则 | 否(修改原有代码) | 是(扩展新增,不改旧代码) |
责任链模式的关键实现要点:
appendNext()返回this:支持链式调用,是优雅组装责任链的语法糖。- 每个节点的三判断模式 :① 当前节点是否满足处理条件?不满足 → 提前返回。② 有无下一节点?无 → 返回最终结果。③ 传递给下一节点
next.doAuth()。- 链的可配置化:本案例是代码硬组装,生产中常见的方式是从数据库/配置文件读取审批链配置,动态组装,实现完全可配置的流程引擎。
责任链模式的适用场景:
| 场景 | 说明 |
|---|---|
| 多级审批流程 | 报销审批、上线审批、权限申请等 |
| HTTP 过滤器/拦截器链 | javax.servlet.Filter 就是责任链 |
| Spring Security 过滤器链 | 多个 SecurityFilter 串成链 |
| Netty ChannelPipeline | 入站/出站处理器链 |
| 日志级别过滤 | 多个 Logger 节点判断日志级别 |
与其他模式对比:
- 责任链模式 vs 装饰器模式 :装饰器的每个节点都会 执行(叠加功能),责任链的节点可以中断链路(提前返回)
- 责任链模式 vs 组合模式:组合模式是树形结构,可双向遍历;责任链是线性结构,单向传递
- 责任链模式 vs 策略模式 :策略在一组算法中选择一个 执行,责任链是顺序依次流过各节点