设计模式--责任链模式 (Chain of Responsibility)
责任链模式将请求沿处理者链传递,解耦了发送者和接收者。处理者可选择处理或传递请求,使处理流程灵活可配。
责任链模式(Chain of Responsibility Pattern)是一种行为设计模式,它允许你将请求沿着处理者链进行发送。收到请求后,每个处理者均可对请求进行处理,或将其传递给链中的下一个处理者。
这种模式的核心思想是:为请求创建了一个接收者对象的链,从而避免请求的发送者和接收者之间的耦合。
意图 / 解决的问题
- 解耦发送者与接收者:请求的发送方不需要知道哪一个对象会最终处理这个请求。它只需将请求发送到链的第一个节点即可。
- 动态组合责任:可以动态地改变链中的成员或调动它们的次序,增加了处理请求的灵活性。
- 单一职责原则:每个处理者只需要关注自己的处理逻辑,而不需要知道链的整体结构。
核心角色
- Handler(抽象处理者):定义一个处理请求的接口。通常会包含一个指向下一个处理者的引用(后继者),以及一个处理请求的抽象方法。
- ConcreteHandler(具体处理者):实现抽象处理者接口。它需要判断自己是否能处理当前请求。如果能,就进行处理;如果不能,就将请求转发给它的后继者。
- Client(客户端):创建并组装处理者链,然后向链的第一个对象发起请求。
结构图
plaintext
+-----------+ +-------------------+ +-------------------+
| Client |------>| ConcreteHandlerA |------>| ConcreteHandlerB |------> ...
+-----------+ +-------------------+ +-------------------+
| |
| (implements) | (implements)
▼ ▼
+----------------+
| Handler |
+----------------+
| + setNext() |
| + handle() |
| - nextHandler |
+----------------+
工作流程
- 客户端创建一个请求,并将其发送给链的第一个处理者(例如
ConcreteHandlerA)。 - 处理者
A接收到请求,判断自己能否处理。- 如果能处理:它就处理该请求,处理流程结束(这是一种“纯”责任链,请求只被一个处理者处理)。
- 如果不能处理:它会检查是否存在下一个处理者(
nextHandler)。- 如果存在,它就将请求传递给下一个处理者
B。 - 如果不存在(即它是链的末端),请求可能会被丢弃或进行默认处理。
- 如果存在,它就将请求传递给下一个处理者
- 处理者
B重复步骤 2,以此类推,直到请求被处理或整个链都遍历完毕。
代码示例:请假审批流程
这是一个非常经典的责任链模式应用场景。假设一个公司的请假审批流程如下:
- 项目组长(GroupLeader)可以批准 2 天及以内的假期。
- 部门经理(DepartmentManager)可以批准 5 天及以内的假期。
- CEO 可以批准所有假期。
1. 定义请求对象 (LeaveRequest)
java
// 请假请求
public class LeaveRequest {
private String name; // 请假人
private int days; // 请假天数
public LeaveRequest(String name, int days) {
this.name = name;
this.days = days;
}
public String getName() {
return name;
}
public int getDays() {
return days;
}
}
2. 定义抽象处理者 (Approver)
java
// 抽象审批者
public abstract class Approver {
protected Approver nextApprover; // 下一个审批者
// 设置下一个审批者,形成链
public void setNextApprover(Approver nextApprover) {
this.nextApprover = nextApprover;
}
// 抽象的处理方法
public abstract void approve(LeaveRequest request);
}
3. 创建具体处理者
项目组长 (GroupLeader)
java
public class GroupLeader extends Approver {
@Override
public void approve(LeaveRequest request) {
if (request.getDays() <= 2) {
System.out.println("项目组长 批准了 " + request.getName() + " 的 " + request.getDays() + " 天假期。");
} else {
// 如果自己处理不了,并且有下一个处理者,就转交
if (nextApprover != null) {
System.out.println("项目组长 无法处理,转交给上级。");
nextApprover.approve(request);
} else {
System.out.println("审批流程结束,无人能处理该请求。");
}
}
}
}
部门经理 (DepartmentManager)
java
public class DepartmentManager extends Approver {
@Override
public void approve(LeaveRequest request) {
if (request.getDays() <= 5) {
System.out.println("部门经理 批准了 " + request.getName() + " 的 " + request.getDays() + " 天假期。");
} else {
if (nextApprover != null) {
System.out.println("部门经理 无法处理,转交给上级。");
nextApprover.approve(request);
} else {
System.out.println("审批流程结束,无人能处理该请求。");
}
}
}
}
CEO
java
public class CEO extends Approver {
@Override
public void approve(LeaveRequest request) {
// CEO 可以批准所有请求
System.out.println("CEO 批准了 " + request.getName() + " 的 " + request.getDays() + " 天假期。");
}
}
4. 客户端组装并测试
java
public class Client {
public static void main(String[] args) {
// 1. 创建处理者
Approver groupLeader = new GroupLeader();
Approver manager = new DepartmentManager();
Approver ceo = new CEO();
// 2. 组装责任链
groupLeader.setNextApprover(manager);
manager.setNextApprover(ceo);
// 3. 创建并提交请求
System.out.println("--- 张三请假 1 天 ---");
groupLeader.approve(new LeaveRequest("张三", 1));
System.out.println("\n--- 李四请假 4 天 ---");
groupLeader.approve(new LeaveRequest("李四", 4));
System.out.println("\n--- 王五请假 10 天 ---");
groupLeader.approve(new LeaveRequest("王五", 10));
}
}
输出结果:
plaintext
--- 张三请假 1 天 ---
项目组长 批准了 张三 的 1 天假期。
--- 李四请假 4 天 ---
项目组长 无法处理,转交给上级。
部门经理 批准了 李四 的 4 天假期。
--- 王五请假 10 天 ---
项目组长 无法处理,转交给上级。
部门经理 无法处理,转交给上级。
CEO 批准了 王五 的 10 天假期。
优缺点
优点
- 降低耦合度:将请求的发送者和接收者解耦。
- 增强了系统的可扩展性:可以很容易地增加新的处理者到链中,而无需修改原有代码,符合开闭原则。
- 增强了指派职责的灵活性:通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。
- 简化了对象:使得每个对象都无需知道链的结构,只需要保存一个指向其后继者的引用。
缺点
- 请求不保证被处理:由于请求在链中传递,如果链的末端处理者也无法处理该请求,那么请求就会被丢弃。
- 性能问题:在链条比较长的情况下,请求的传递会带来一定的性能开销。
- 调试不方便:由于链条是动态组合的,在调试时可能不容易观察到运行时的真实链结构,从而难以定位问题。
适用场景
- 有多个对象可以处理同一个请求,但具体由哪个对象处理是在运行时动态确定的。
- 你想在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
- 需要动态指定一组对象处理请求的顺序。
现实世界中的例子
- Java Web 中的 Servlet Filter:
FilterChain就是一个典型的责任链。每个Filter都可以对Request和Response进行处理,然后调用chain.doFilter()将请求传递给下一个Filter。这是一种变体,因为每个过滤器都会执行,而不仅仅是找到第一个能处理的就停止。 - Web 框架的中间件(Middleware):例如 Express.js、ASP.NET Core 中的中间件管道,请求会依次通过管道中的每个中间件,每个中间件都可以处理请求、修改请求或提前返回响应。
- UI 事件冒泡:在前端开发中,一个事件(如点击)会从最内层的元素开始,逐级向上传播到父元素,每个元素都可以捕获并处理这个事件。
总结
责任链模式的核心在于解耦和灵活性。它将处理逻辑分布到一串独立的对象中,使得系统在应对需求变化时,能够通过重组这条“链”来快速适应,而不需要修改核心业务代码。
右滑查看面试常问