用「公司审批流程」讲透核心逻辑
责任链模式的讲解沿用「生活场景→角色映射→代码实现→核心总结」的结构,避免 "为了用模式而用模式",聚焦实际业务价值。
一、先看一个你肯定懂的生活场景
假设你在公司申请请假,流程是这样的:
- 请假 1 天以内:部门经理直接审批;
- 请假 1-3 天(含 3 天):部门经理无权审批,传给总监审批;
- 请假 3 天以上:总监也无权审批,传给总经理审批;
- 没有审批者能处理时(比如请假 100 天,超出所有权限),申请被驳回。
这个流程就是「责任链模式」的天然原型:
- 「请假申请」是「请求」;
- 「部门经理、总监、总经理」是「处理者」,按顺序连成一条「链」;
- 你(申请人)只需要把请假申请交给「链的第一个处理者(部门经理)」,不用关心谁最终审批,也不用知道链上有多少人 ------ 这就是责任链的核心:解耦请求发送者和接收者。
二、责任链模式的核心角色(对应审批场景)
不管是请假审批,还是 Web 开发的过滤器,责任链的核心角色永远只有 3 个,和场景一一对应:
| 模式角色 | 对应请假场景中的角色 | 核心职责 |
|---|---|---|
| 抽象处理者(Handler) | 抽象审批者(AbstractApprover) | 定义处理请求的接口(比如 "审批请假"),持有「下一个处理者」的引用(比如经理知道下一个是总监)。 |
| 具体处理者(ConcreteHandler) | 部门经理、总监、总经理 | 实现抽象接口,判断自己能否处理请求:能处理就直接处理,不能就传给下一个处理者。 |
| 客户端(Client) | 请假的员工 | 创建所有处理者,串联成链(比如指定 "经理→总监→总经理"),发起请求(提交请假申请)。 |
核心逻辑一句话:请求沿着链传递,直到被处理或链结束(两种情况:要么有人处理,要么无人处理被驳回)。
三、代码实现:把请假流程写成可运行的代码
我们用 Java 实现这个请假审批流程,代码简化且贴近业务,每个步骤都带注释,比日志例子更易理解:
步骤 1:定义抽象处理者(抽象审批者)
统一所有审批者的行为规范,包含「处理请求」和「设置下一个审批者」的核心方法:
java
运行
// 抽象处理者:抽象审批者
abstract class AbstractApprover {
// 下一个审批者(责任链的"链节")
protected AbstractApprover nextApprover;
// 审批者名称(方便打印日志)
protected String name;
public AbstractApprover(String name) {
this.name = name;
}
// 设置下一个审批者(串联链)
public void setNextApprover(AbstractApprover nextApprover) {
this.nextApprover = nextApprover;
}
// 核心方法:处理请假请求(子类必须实现)
public abstract void processLeaveRequest(LeaveRequest request);
}
步骤 2:定义请求对象(请假申请)
封装请求的核心信息,避免参数零散(实际开发中可扩展更多字段,比如请假原因、申请人部门):
java
运行
// 请求对象:请假申请
class LeaveRequest {
private String applicant; // 申请人
private int days; // 请假天数
public LeaveRequest(String applicant, int days) {
this.applicant = applicant;
this.days = days;
}
// getter方法(方便审批者获取信息)
public String getApplicant() { return applicant; }
public int getDays() { return days; }
}
步骤 3:实现具体处理者(3 个审批者)
每个审批者只关注自己的「审批权限」,无权则直接传递给下一个,逻辑清晰不冗余:
java
运行
// 具体处理者1:部门经理(审批1天以内)
class DeptManager extends AbstractApprover {
public DeptManager(String name) {
super(name);
}
@Override
public void processLeaveRequest(LeaveRequest request) {
int days = request.getDays();
String applicant = request.getApplicant();
// 自己能处理的情况:1天以内
if (days <= 1) {
System.out.println(String.format("部门经理【%s】审批通过:%s请假%d天(1天内权限)",
name, applicant, days));
} else {
// 无权处理,传给下一个审批者
System.out.println(String.format("部门经理【%s】无权审批:%s请假%d天,转交总监",
name, applicant, days));
if (nextApprover != null) {
nextApprover.processLeaveRequest(request);
} else {
// 没有下一个处理者,请求被驳回
System.out.println("审批链断裂,请假申请驳回");
}
}
}
}
// 具体处理者2:总监(审批1-3天)
class Director extends AbstractApprover {
public Director(String name) {
super(name);
}
@Override
public void processLeaveRequest(LeaveRequest request) {
int days = request.getDays();
String applicant = request.getApplicant();
if (days > 1 && days <= 3) {
System.out.println(String.format("总监【%s】审批通过:%s请假%d天(1-3天权限)",
name, applicant, days));
} else {
System.out.println(String.format("总监【%s】无权审批:%s请假%d天,转交总经理",
name, applicant, days));
if (nextApprover != null) {
nextApprover.processLeaveRequest(request);
} else {
System.out.println("审批链断裂,请假申请驳回");
}
}
}
}
// 具体处理者3:总经理(审批3天以上)
class GeneralManager extends AbstractApprover {
public GeneralManager(String name) {
super(name);
}
@Override
public void processLeaveRequest(LeaveRequest request) {
int days = request.getDays();
String applicant = request.getApplicant();
if (days > 3) {
System.out.println(String.format("总经理【%s】审批通过:%s请假%d天(3天以上权限)",
name, applicant, days));
} else {
// 理论上不会走到这(因为前两个审批者已经处理了3天内的请求)
System.out.println(String.format("总经理【%s】无权审批:%s请假%d天,无后续审批者",
name, applicant, days));
}
}
}
步骤 4:客户端(申请人)创建链并发起请求
客户端只做两件事:1. 把审批者连成链;2. 提交请求给链的第一个节点,不用管后续流程:
java
运行
public class ChainOfResponsibilityDemo {
public static void main(String[] args) {
// 1. 创建3个具体处理者(审批者)
AbstractApprover deptManager = new DeptManager("张三");
AbstractApprover director = new Director("李四");
AbstractApprover gm = new GeneralManager("王五");
// 2. 串联成责任链:部门经理 → 总监 → 总经理
deptManager.setNextApprover(director);
director.setNextApprover(gm);
// 3. 发起3个不同的请假请求(测试链的处理逻辑)
System.out.println("=== 第一个请求:请假1天 ===");
deptManager.processLeaveRequest(new LeaveRequest("小明", 1));
System.out.println("\n=== 第二个请求:请假2天 ===");
deptManager.processLeaveRequest(new LeaveRequest("小红", 2));
System.out.println("\n=== 第三个请求:请假5天 ===");
deptManager.processLeaveRequest(new LeaveRequest("小刚", 5));
}
}
四、运行结果:直观看到请求传递过程
plaintext
=== 第一个请求:请假1天 ===
部门经理【张三】审批通过:小明请假1天(1天内权限)
=== 第二个请求:请假2天 ===
部门经理【张三】无权审批:小红请假2天,转交总监
总监【李四】审批通过:小红请假2天(1-3天权限)
=== 第三个请求:请假5天 ===
部门经理【张三】无权审批:小刚请假5天,转交总监
总监【李四】无权审批:小刚请假5天,转交总经理
总经理【王五】审批通过:小刚请假5天(3天以上权限)
这个结果和我们生活中的审批流程完全一致,你能清晰看到:请求沿着链传递,直到找到能处理它的节点,客户端(申请人)全程只对接了部门经理,根本不用关心后续是谁审批。
五、核心总结:责任链模式到底解决什么问题?
1. 核心价值(对应请假场景的好处)
- 解耦:申请人不用记 "谁能批几天假",只需找第一个审批者(部门经理),减少沟通成本;
- 灵活:如果公司新增 "副总监" 审批 2-5 天,只需新增一个
ViceDirector类,修改链的顺序(部门经理→副总监→总监→总经理),不用改原有审批者的代码; - 简化对象:每个审批者只关心自己的权限,不用知道整个链的结构(比如部门经理不用知道总经理的存在)。
2. 缺点(实际开发中要注意)
- 请求可能无人处理:比如请假 100 天,所有审批者都无权处理,此时需要在链的最后加一个 "默认处理者"(比如 HR 驳回);
- 性能影响:如果链很长(比如 10 个审批者),请求要依次传递,会有轻微性能损耗;
- 调试麻烦:如果请求传递出错(比如链断了),需要逐节点排查,不如直接调用直观。
3. 实际开发场景(不再是 "刻意用模式")
- Web 开发的 Filter 链:比如 Tomcat 处理请求时,请求会经过多个 Filter(编码过滤、权限过滤、日志过滤),每个 Filter 处理完后传给下一个;
- 框架的拦截器链:比如 Struts2、Spring MVC 的拦截器,请求先经过拦截器,再到 Controller;
- 工作流引擎:比如 OA 系统的报销审批、合同审批,本质都是责任链的延伸(可动态配置链的节点)。
六、和策略模式的区别(避免混淆)
很多人会把责任链和策略模式搞混,用一句话区分:
- 策略模式:多个处理方案选一个(比如出行选飞机 / 火车 / 汽车,只能选一种);
- 责任链模式:多个处理者传一个请求(比如请假申请传给经理 / 总监 / 总经理,直到有人处理)。
简单说:策略模式是 "选择",责任链是 "传递"。
到这里,责任链模式的核心逻辑就讲透了 ------ 它不是一个 "刻意设计" 的复杂模式,而是生活中 "流程传递" 场景的代码抽象,核心就是「解耦发送者和接收者,让请求沿着链自动找到处理者」。