责任链模式
责任链模式是一种行为型设计模式。它的核心思想是:
- 将一系列处理请求的对象连成一条链。
- 请求沿着这条链传递,直到某个对象处理它。
- 发送请求的对象不需要知道链上的具体处理者是谁。
- 每个处理者只负责自己能处理的部分,不能处理则传递给下一个。
通俗理解:
想象一个审批流程,员工提交请假申请,首先交给直属主管审批,如果主管不处理或条件不满足,就自动传递到部门经理,再传递到人力资源审批,直到处理完成。
责任链模式解决的问题:
- 解耦发送者和接收者:发送请求的对象无需知道处理请求的具体类。
- 动态添加处理节点:链条可以在运行时动态构建。
- 可以自由选择处理顺序:责任链中的节点顺序可调整。
- 增强灵活性和扩展性:增加新的处理者节点,不会影响现有代码。
典型应用场景:
- 审批流程(多级审批、请假申请、报销)
- 日志过滤(INFO、WARN、ERROR)
- 请求拦截(Servlet Filter、Spring Interceptor)
- 事件处理(GUI事件传递、观察者链)
责任链一般都包含什么
- Handler(处理者抽象类)
- 定义处理请求的接口和持有下一个处理者的引用。
- 可以有一个
next指针指向下一个节点。 - 定义
handleRequest()抽象方法,由具体子类实现。
- ConcreteHandler(具体处理者)
- 继承 Handler,实现具体的处理逻辑。
- 可以根据条件决定是否处理请求或传递给下一个节点。
- Client(客户端)
- 构建责任链。
- 发起请求,将请求传入链条的入口。
- Request(请求对象,可选)
- 封装请求参数,传递给责任链节点。
- 在你的示例里就是
AuthInfo返回的审批信息,也可以是请求封装对象。
责任链结构图可以抽象如下:
Client -> Handler1 -> Handler2 -> Handler3 -> ...
- Client 不知道具体哪个 Handler 会处理请求。
- 每个 Handler 自己判断能否处理,不能处理就交给 next。
责任链模式核心设计
- 链条是动态可扩展的
- 通过
appendNext或者构造函数动态组合节点。
- 通过
- 节点内逻辑独立
- 每个节点只关注自己处理的业务逻辑,不依赖其他节点。
- 递归或循环传递请求
- 节点处理完后,如果有下一个节点,递归调用下一节点处理请求。
- 可停止链条
- 如果某个节点处理完成并满足条件,可以选择不传递给下一个节点。
- 支持返回结果
- 可以返回处理结果,或者继续沿链传递。
示例
假设我们当前有一个需求就是设计层层审批 最简单的结果应该是
java
public class AuthController {
private SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 时间格式化
public AuthInfo doAuth(String uId, String orderId, Date authDate) throws ParseException {
// 三级审批
Date date = AuthService.queryAuthInfo("1000013", orderId);
if (null == date) return new AuthInfo("0001", "单号:", orderId, " 状态:待三级审批负责人 ", "王工");
// 二级审批
if (authDate.after(f.parse("2020-06-01 00:00:00")) && authDate.before(f.parse("2020-06-25 23:59:59"))) {
date = AuthService.queryAuthInfo("1000012", orderId);
if (null == date) return new AuthInfo("0001", "单号:", orderId, " 状态:待二级审批负责人 ", "张经理");
}
// 一级审批
if (authDate.after(f.parse("2020-06-11 00:00:00")) && authDate.before(f.parse("2020-06-20 23:59:59"))) {
date = AuthService.queryAuthInfo("1000011", orderId);
if (null == date) return new AuthInfo("0001", "单号:", orderId, " 状态:待一级审批负责人 ", "段总");
}
return new AuthInfo("0001", "单号:", orderId, " 状态:审批完成");
}
}
像是这样的功能看起来很简单的,但是实际的业务中会有很多部门,但如果这样实现就很难进行扩展,并且在改动扩展调整也非常麻烦。


上图是这个业务模型中责任链结构的核心部分,通过三个实现了统一抽象类AuthLink的不同规则,再进行责任编排模拟出一条链路。这个链路就是业务中的责任链。
一般在使用责任链时候如果是场景比较固定,可以通过写死到代码中进行初始化。但如果业务场景经常变化可以做成xml配置的方式进行处理,也可以落到库里进行初始化操作。
java
public class AuthInfo {
private String code;
private String info = "";
public AuthInfo(String code, String ...infos) {
this.code = code;
for (String str:infos){
this.info = this.info.concat(str);
}
}
// ...get/set
}//定义责任链模式
抽象类定义
java
public abstract class AuthLink {
protected Logger logger = LoggerFactory.getLogger(AuthLink.class);
protected SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 时间格式化
protected String levelUserId; // 级别人员ID
protected String levelUserName; // 级别人员姓名
private AuthLink next; // 责任链
public AuthLink(String levelUserId, String levelUserName) {
this.levelUserId = levelUserId;
this.levelUserName = levelUserName;
}
public AuthLink next() {
return next;
}
public AuthLink appendNext(AuthLink next) {
this.next = next;
return this;
}
public abstract AuthInfo doAuth(String uId, String orderId, Date authDate);
}
为什么需要抽象类
责任链模式的设计思想:
- 每个节点只处理自己的逻辑。
- 节点可以传递请求给下一个节点。
- 链条是动态可扩展的。
如果你直接写一个类让 Client 调接口,会出现几个问题:
-
所有审批逻辑都写在同一个类里 → 违背单一职责原则
例如一级、二级、三级审批都在同一个类里,你要处理修改审批逻辑就要改这个类,任何小改动都会影响整体。
-
链条不灵活 → 增加新级别审批时必须改原类
你如果想新增四级审批,就要修改已有类逻辑,破坏开闭原则。
-
难以复用 → 不同业务场景不能复用节点
你可能还想在其他流程里复用一级审批逻辑,如果所有逻辑都写在一个类里,你无法单独调用。
抽象类 AuthLink 的作用就是定义责任链节点的统一接口:
- 定义了
doAuth()抽象方法,每个具体节点必须实现。 - 持有
next指针,实现责任链传递机制。 - 提供
appendNext()方法,让 Client 可以动态组合节点。
抽象类不是强制要求必须用,但它提供了统一规范:
- 任何节点都必须实现
doAuth()。 - 节点都可以使用
next传递请求,无需每个节点重复实现。
如果你不用抽象类,而直接写一个类,每个节点就可能变成"写死"的方法:
java
public class AuthService {
public AuthInfo doLevel1() {...}
public AuthInfo doLevel2() {...}
public AuthInfo doLevel3() {...}
}
这就不是责任链了,而是顺序调用,失去了责任链的灵活性。
每个实现类 Level1AuthLink、Level2AuthLink、Level3AuthLink:
-
独立处理自己的业务逻辑
每一级审批逻辑不同(时间段、判断条件、审批人),独立实现。
-
可复用
例如你在另一个流程也要用一级审批,只需实例化
Level1AuthLink。 -
可以灵活组合链条
Client 可以决定是一级→二级→三级,还是一级→三级跳过二级。
假设你写一个单类接口:
java
AuthInfo doAuth(String uId, String orderId, Date authDate);
然后内部写死了一级、二级、三级审批:
- 链条无法动态调整顺序
- 新增节点必须改原类
- 节点间逻辑耦合度高
- 复用性差
责任链模式强调的"链条动态传递 + 节点独立"就被破坏了
实现类定义
java
public class Level1AuthLink extends AuthLink {
public Level1AuthLink(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.next();
if (null == next) {
return new AuthInfo("0000", "单号:", orderId, " 状态:一级审批完成负责人", " 时间:", f.format(date), " 审批人:", levelUserName);
}
return next.doAuth(uId, orderId, authDate);
}
}
java
public class Level2AuthLink extends AuthLink {
private Date beginDate = f.parse("2020-06-11 00:00:00");
private Date endDate = f.parse("2020-06-20 23:59:59");
public Level2AuthLink(String levelUserId, String levelUserName) throws ParseException {
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.next();
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);
}
}
java
public class Level3AuthLink 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 Level3AuthLink(String levelUserId, String levelUserName) throws ParseException {
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.next();
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);
}
}
- 如上三个类;
Level1AuthLink、Level2AuthLink、Level3AuthLink,实现了不同的审核级别处理的简单逻辑。 - 例如第一个审核类中会先判断是否审核通过,如果没有审核通过则返回结果给调用方,引导去审核。(这里简单模拟审核后有时间信息不为空,作为判断条件)
- 判断完成后获取下一个审核节点;
super.next();,如果不存在下一个节点,则直接返回结果。 - 之后是根据不同的业务时间段进行判断是否需要,二级和一级的审核。
- 最后返回下一个审核结果;
next.doAuth(uId, orderId, authDate);,有点像递归调用