设计模式-责任链模式:让请求在链条中流动直到被处理
在日常开发中,我们经常遇到这样的场景:一个请求需要经过多个对象的处理,每个对象都有可能处理这个请求,但具体由哪个对象处理在运行时才能确定。比如请假审批流程、过滤敏感词、异常处理等。如果使用传统的if-else嵌套,代码会变得臃肿且难以维护。
有一种优雅解决这类问题的设计模式------责任链模式。
1. 什么是责任链模式?
责任链模式是一种行为设计模式,它允许你将请求沿着处理者链进行发送。收到请求后,每个处理者都可以处理请求,或将其传递给链上的下一个处理者。
简单来说,责任链模式的核心思想是:
- 多个处理者:将多个处理者对象连接成一条链
- 传递请求:请求沿着这条链传递,直到被某个处理者处理
- 动态处理:处理者可以动态地增加或减少,处理顺序也可以动态调整
2. 从一个实际问题开始
假设我们要实现一个请假审批系统:
- 1天以内的请假,组长可以审批
- 3天以内的请假,经理可以审批
- 7天以内的请假,总监可以审批
- 超过7天的请假,需要CEO审批
如果不使用设计模式,我们可能会写出这样的代码:
java
// 反面教材:使用复杂的if-else嵌套
public class LeaveApproval {
public boolean approve(LeaveRequest request) {
int days = request.getDays();
if (days <= 1) {
// 组长审批逻辑
System.out.println("组长审批通过");
return true;
} else if (days <= 3) {
// 经理审批逻辑
System.out.println("经理审批通过");
return true;
} else if (days <= 7) {
// 总监审批逻辑
System.out.println("总监审批通过");
return true;
} else {
// CEO审批逻辑
System.out.println("CEO审批通过");
return true;
}
}
}
这段代码的问题:
- 违反开闭原则:新增审批级别需要修改原有代码
- 职责不清晰:一个方法负责了所有审批逻辑
- 难以维护:随着审批规则复杂化,代码会越来越臃肿
3. 责任链模式的结构
责任链模式包含三个核心角色:
nextHandler
nextHandler
使用
<<interface>>
Handler
+setNext(handler) : void
+handle(request) : void
ConcreteHandlerA
-nextHandler: Handler
+handle(request) : void
ConcreteHandlerB
-nextHandler: Handler
+handle(request) : void
Client
+sendRequest(request) : void
- 抽象处理者:定义处理请求的接口,通常包含设置下一个处理者的方法
- 具体处理者:实现抽象处理者,处理自己负责的请求,不能处理则传递给下一个
- 客户端:创建处理链,并向链头发送请求
4. 责任链模式的Java实现
让我们用责任链模式重构请假审批系统:
步骤1:定义请求类
java
/**
* 请假请求类
*/
public class LeaveRequest {
private String employeeName; // 员工姓名
private int days; // 请假天数
private String reason; // 请假原因
public LeaveRequest(String employeeName, int days, String reason) {
this.employeeName = employeeName;
this.days = days;
this.reason = reason;
}
// Getter方法
public String getEmployeeName() {
return employeeName;
}
public int getDays() {
return days;
}
public String getReason() {
return reason;
}
}
步骤2:定义抽象处理者
java
/**
* 抽象审批者(处理者)
*/
public abstract class Approver {
protected Approver nextApprover; // 下一个审批者
protected String name; // 审批者姓名
protected int maxDays; // 最大审批天数
public Approver(String name, int maxDays) {
this.name = name;
this.maxDays = maxDays;
}
/**
* 设置下一个审批者
*/
public Approver setNextApprover(Approver nextApprover) {
this.nextApprover = nextApprover;
return this.nextApprover; // 返回下一个审批者,支持链式调用
}
/**
* 处理请假请求
*/
public void processRequest(LeaveRequest request) {
if (request.getDays() <= maxDays) {
// 能够处理该请求
System.out.println(name + "审批通过");
System.out.println("员工:" + request.getEmployeeName());
System.out.println("天数:" + request.getDays() + "天");
System.out.println("原因:" + request.getReason());
System.out.println("-----------------------");
} else if (nextApprover != null) {
// 传递给下一个审批者
System.out.println(name + "无法审批,转交给上级");
nextApprover.processRequest(request);
} else {
// 没有下一个审批者,无法处理
System.out.println("请假天数过长,无人能够审批");
System.out.println("员工:" + request.getEmployeeName());
System.out.println("天数:" + request.getDays() + "天(超过最大审批天数)");
System.out.println("-----------------------");
}
}
}
步骤3:实现具体处理者
java
/**
* 组长审批者(可以审批1天以内的请假)
*/
public class GroupLeader extends Approver {
public GroupLeader(String name) {
super(name, 1); // 组长最多审批1天
}
}
/**
* 经理审批者(可以审批3天以内的请假)
*/
public class Manager extends Approver {
public Manager(String name) {
super(name, 3); // 经理最多审批3天
}
}
/**
* 总监审批者(可以审批7天以内的请假)
*/
public class Director extends Approver {
public Director(String name) {
super(name, 7); // 总监最多审批7天
}
}
/**
* CEO审批者(可以审批任何天数的请假)
*/
public class CEO extends Approver {
public CEO(String name) {
super(name, Integer.MAX_VALUE); // CEO可以审批任何天数
}
@Override
public void processRequest(LeaveRequest request) {
// CEO特殊处理:需要记录详细日志
System.out.println(name + "(CEO)审批通过 - 重要记录备案");
System.out.println("员工:" + request.getEmployeeName());
System.out.println("天数:" + request.getDays() + "天");
System.out.println("原因:" + request.getReason());
System.out.println("审批时间:" + new java.util.Date());
System.out.println("-----------------------");
}
}
步骤4:客户端使用
java
public class Client {
public static void main(String[] args) {
// 1. 创建审批链
System.out.println("=== 创建审批链 ===");
Approver groupLeader = new GroupLeader("张组长");
Approver manager = new Manager("李经理");
Approver director = new Director("王总监");
Approver ceo = new CEO("陈总");
// 构建责任链:组长 -> 经理 -> 总监 -> CEO
groupLeader.setNextApprover(manager);
manager.setNextApprover(director);
director.setNextApprover(ceo);
// 2. 创建请假请求
System.out.println("\n=== 处理请假请求 ===");
// 请假半天 - 组长审批
LeaveRequest request1 = new LeaveRequest("张三", 1, "感冒发烧");
groupLeader.processRequest(request1);
// 请假2天 - 经理审批
LeaveRequest request2 = new LeaveRequest("李四", 2, "家里有事");
groupLeader.processRequest(request2);
// 请假5天 - 总监审批
LeaveRequest request3 = new LeaveRequest("王五", 5, "旅游度假");
groupLeader.processRequest(request3);
// 请假10天 - CEO审批
LeaveRequest request4 = new LeaveRequest("赵六", 10, "婚假");
groupLeader.processRequest(request4);
// 3. 动态修改责任链
System.out.println("\n=== 动态修改责任链(跳过总监) ===");
// 修改链:组长 -> 经理 -> CEO
manager.setNextApprover(ceo);
LeaveRequest request5 = new LeaveRequest("孙七", 6, "病假");
groupLeader.processRequest(request5);
}
}
输出结果:
=== 创建审批链 ===
=== 处理请假请求 ===
张组长审批通过
员工:张三
天数:1天
原因:感冒发烧
-----------------------
张组长无法审批,转交给上级
李经理审批通过
员工:李四
天数:2天
原因:家里有事
-----------------------
张组长无法审批,转交给上级
李经理无法审批,转交给上级
王总监审批通过
员工:王五
天数:5天
原因:旅游度假
-----------------------
张组长无法审批,转交给上级
李经理无法审批,转交给上级
王总监无法审批,转交给上级
陈总(CEO)审批通过 - 重要记录备案
员工:赵六
天数:10天
原因:婚假
审批时间:Tue Oct 10 14:30:00 CST 2023
-----------------------
=== 动态修改责任链(跳过总监) ===
张组长无法审批,转交给上级
李经理无法审批,转交给上级
陈总(CEO)审批通过 - 重要记录备案
员工:孙七
天数:6天
原因:病假
审批时间:Tue Oct 10 14:30:05 CST 2023
-----------------------
5. 责任链模式的变体
5.1 纯的责任链 vs 不纯的责任链
纯的责任链:一个请求必须被某个处理者处理,不能出现无处理者的情况。
java
public abstract class PureApprover {
protected PureApprover nextApprover;
// 纯责任链:必须处理请求,不能传递
public abstract void handleRequest(LeaveRequest request);
}
不纯的责任链:允许请求不被处理,或者被多个处理者处理。
java
public abstract class ImpureApprover {
protected ImpureApprover nextApprover;
// 不纯责任链:可以选择处理或传递
public void handleRequest(LeaveRequest request) {
// 1. 自己先处理一部分
doSomething(request);
// 2. 传递给下一个(可选)
if (nextApprover != null && canPass(request)) {
nextApprover.handleRequest(request);
}
}
protected abstract void doSomething(LeaveRequest request);
protected abstract boolean canPass(LeaveRequest request);
}
5.2 使用链式调用构建责任链
java
/**
* 使用链式调用构建责任链
*/
public class ChainBuilder {
public static void main(String[] args) {
// 链式构建责任链
Approver chain = new GroupLeader("组长")
.setNextApprover(new Manager("经理"))
.setNextApprover(new Director("总监"))
.setNextApprover(new CEO("CEO"));
// 使用构建好的链
LeaveRequest request = new LeaveRequest("员工", 4, "事假");
chain.processRequest(request);
}
}
5.3 使用集合管理责任链
java
/**
* 使用集合管理多个处理者
*/
public class ApproverChain {
private List<Approver> approvers = new ArrayList<>();
private int index = 0;
public void addApprover(Approver approver) {
approvers.add(approver);
}
public void processRequest(LeaveRequest request) {
if (index < approvers.size()) {
Approver approver = approvers.get(index);
index++;
if (request.getDays() <= approver.getMaxDays()) {
approver.processRequest(request);
} else {
processRequest(request); // 递归调用下一个
}
} else {
System.out.println("无人能够审批该请求");
}
}
public void reset() {
index = 0;
}
}
6. 责任链模式的优势
6.1 对比原始实现
| 对比维度 | 原始实现(if-else) | 责任链模式 |
|---|---|---|
| 新增审批级别 | 需要修改原有代码 | 只需新增一个处理者类 |
| 调整审批顺序 | 需要修改代码逻辑 | 只需调整链的顺序 |
| 代码结构 | 一个方法包含所有逻辑 | 逻辑分散到各个处理者 |
| 可测试性 | 难以单独测试每种情况 | 每个处理者可独立测试 |
| 遵循原则 | 违反开闭原则 | 符合开闭原则、单一职责原则 |
6.2 核心优势
- 降低耦合度:请求发送者不需要知道具体哪个对象处理请求
- 增强灵活性:可以动态地增加、删除或重新排列处理者
- 简化对象职责:每个处理者只负责自己的处理逻辑
- 符合开闭原则:新增处理者不需要修改现有代码
7. 责任链模式的实际应用场景
场景1:过滤器链(如Servlet Filter)
java
// 模拟Servlet Filter的责任链
public interface Filter {
void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain);
}
public class FilterChain {
private List<Filter> filters = new ArrayList<>();
private int index = 0;
public void addFilter(Filter filter) {
filters.add(filter);
}
public void doFilter(HttpServletRequest request, HttpServletResponse response) {
if (index < filters.size()) {
Filter filter = filters.get(index);
index++;
filter.doFilter(request, response, this);
}
}
}
// 具体过滤器
public class LoggingFilter implements Filter {
@Override
public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) {
System.out.println("日志过滤器:记录请求 " + request.getRequestURI());
chain.doFilter(request, response);
System.out.println("日志过滤器:记录响应");
}
}
public class AuthFilter implements Filter {
@Override
public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) {
System.out.println("认证过滤器:检查用户登录状态");
if (isAuthenticated(request)) {
chain.doFilter(request, response);
} else {
response.setStatus(401);
}
}
private boolean isAuthenticated(HttpServletRequest request) {
// 检查认证逻辑
return true;
}
}
场景2:异常处理链
java
// 异常处理责任链
public abstract class ExceptionHandler {
protected ExceptionHandler nextHandler;
public void setNextHandler(ExceptionHandler nextHandler) {
this.nextHandler = nextHandler;
}
public void handle(Exception exception) {
if (canHandle(exception)) {
doHandle(exception);
} else if (nextHandler != null) {
nextHandler.handle(exception);
} else {
System.out.println("没有处理器能够处理该异常: " + exception.getClass().getName());
}
}
protected abstract boolean canHandle(Exception exception);
protected abstract void doHandle(Exception exception);
}
// 具体异常处理器
public class NullPointerExceptionHandler extends ExceptionHandler {
@Override
protected boolean canHandle(Exception exception) {
return exception instanceof NullPointerException;
}
@Override
protected void doHandle(Exception exception) {
System.out.println("处理空指针异常: " + exception.getMessage());
// 具体的处理逻辑...
}
}
public class IOExceptionHandler extends ExceptionHandler {
@Override
protected boolean canHandle(Exception exception) {
return exception instanceof IOException;
}
@Override
protected void doHandle(Exception exception) {
System.out.println("处理IO异常: " + exception.getMessage());
// 具体的处理逻辑...
}
}
8. Spring框架中的责任链模式
Spring框架中大量使用了责任链模式,最典型的就是Spring MVC的拦截器链:
java
// Spring MVC拦截器接口(简化版)
public interface HandlerInterceptor {
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler);
void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView);
void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex);
}
// Spring Security过滤器链
public class SecurityFilterChain {
private List<Filter> filters;
public void doFilter(HttpServletRequest request, HttpServletResponse response) {
// 创建过滤器链并执行
VirtualFilterChain chain = new VirtualFilterChain(request, filters);
chain.doFilter(request, response);
}
private static class VirtualFilterChain {
private final List<Filter> additionalFilters;
private int currentPosition = 0;
public void doFilter(HttpServletRequest request, HttpServletResponse response) {
if (currentPosition == additionalFilters.size()) {
// 所有过滤器执行完毕
return;
}
Filter nextFilter = additionalFilters.get(currentPosition);
currentPosition++;
nextFilter.doFilter(request, response, this);
}
}
}
9. 责任链模式的最佳实践
9.1 使用建造者模式构建链
java
/**
* 使用建造者模式构建责任链
*/
public class ApproverChainBuilder {
private Approver first;
private Approver last;
public ApproverChainBuilder addApprover(Approver approver) {
if (first == null) {
first = approver;
last = approver;
} else {
last.setNextApprover(approver);
last = approver;
}
return this;
}
public Approver build() {
return first;
}
// ** 省略部分代码
}
// 使用方式
Approver chain = new ApproverChainBuilder()
.addApprover(new GroupLeader("组长"))
.addApprover(new Manager("经理"))
.addApprover(new Director("总监"))
.build();
9.2 处理链的终止条件
java
/**
* 改进的抽象处理者,支持终止链
*/
public abstract class ImprovedApprover {
protected ImprovedApprover nextApprover;
public void setNextApprover(ImprovedApprover nextApprover) {
this.nextApprover = nextApprover;
}
public void processRequest(LeaveRequest request) {
// 处理请求
boolean handled = handle(request);
// 如果当前处理者没有处理,并且有下一个处理者,则传递
if (!handled && nextApprover != null) {
nextApprover.processRequest(request);
} else if (!handled) {
// 链终止,没有处理者能够处理
System.out.println("请求未被处理: " + request);
}
}
/**
* 处理请求,返回true表示已处理,false表示未处理
*/
protected abstract boolean handle(LeaveRequest request);
}
10. 责任链模式的局限性
- 请求可能不被处理:如果链中没有合适的处理者,请求可能得不到处理
- 性能问题:链过长时,请求需要经过多个处理者,可能影响性能
- 调试困难:请求在链中传递,调试时难以跟踪具体经过哪些处理者
- 可能产生循环引用:如果链形成环,会导致死循环
11. 总结
责任链模式是一种非常实用的设计模式,它通过将多个处理者连接成一条链,让请求沿着链传递,直到被某个处理者处理。这种模式特别适合处理需要经过多个步骤或级别的场景。
责任链模式的核心价值:
- 解耦发送者和接收者:发送者不需要知道具体哪个对象处理请求
- 动态配置处理流程:可以动态地增加、删除或调整处理者顺序
- 提高代码可维护性:每个处理者只关注自己的职责,符合单一职责原则
使用责任链模式时要注意:
- 确保链不会过长,避免性能问题
- 确保请求最终能够被处理,或者有合适的兜底处理
- 注意避免循环引用导致的死循环