设计模式(四)——责任链模式
1. 责任链模式的定义
责任链模式(Chain of Responsibility,简称 CoR,也叫职责链模式)是一种行为型设计模式,允许一个请求在一系列处理器(handlers)中传递。每个处理器可以选择自己处理该请求,或者将其传递给下一个处理器。
一个大家比较熟悉的场景是找售后客服,通常是机器人客服先出来接待,然后在你的多次要求下转给人工客服,如果是设备维修可能还需要转到专业人员。这个场景就很适合应用责任链模式。总结一下就是这个模式适用于设计灵活、解耦的请求处理流水线。
2. 案例分析
2.1 需求
假设你被安排开发一个在线订购系统。在用户下单前,系统需要执行以下检查:
- 验证用户身份(Authenticate)
- 若有需要,校验管理员权限(Authorize)
- 清理和预处理输入数据(Sanitize)
- 阻止重复失败的请求(安全性)
- 如果有缓存则直接返回缓存结果
2.2 潜在问题
如果把所有这些检查逻辑写在一个很长的方法里,你会发现:
- 代码变得冗长混乱,难维护;
- 添加或移除检查逻辑很麻烦;
- 想在别处复用检查逻辑会产生大量重复代码。
2.3 基于责任链的解决方案
责任链模式将每个检查逻辑封装为独立的处理器类(handler),在其中定义 handle()
方法来解决上述问题。
每个处理器可以自行决定:
- 是否处理当前请求;
- 是否将请求传递给下一个处理器。
最后将这些处理器串联起来,形成一个处理流水线(chain)。
2.3.1 类定义
- Handler(处理器接口)
- BaseHandler(可选的抽象基类,提供链式调用逻辑)
- AuthHandler(认证处理器)
- SanitizeHandler(数据清理处理器)
- CacheHandler(缓存处理器)
每个处理器只需要知道下一个处理器,从而让请求顺序向下传递。
2.3.2 具体实现
步骤 1:定义处理器接口
interface Handler {void handle(Request request);
}
步骤 2:创建基础处理器 BaseHandler(可选)
// BaseHandler 提供了公共逻辑,用于链接和转发请求。
// 它实现了 Handler 接口。
abstract class BaseHandler implements Handler {// 保存下一个处理器的引用private Handler next;/*** 设置链中的下一个处理器。* 允许像这样链式调用:auth.setNext(validation).setNext(logger);** @param next 下一个处理器* @return 返回传入的处理器,用于链式调用*/public BaseHandler setNext(Handler next) {this.next = next;return (BaseHandler) next;}/*** 如果存在下一个处理器,则将请求转发给它。** @param request 需要传递的请求对象*/protected void forward(Request request) {if (next != null) {// 委托给下一个处理器next.handle(request); }}
}
BaseHandler
是一个抽象类,不能直接使用,而是由具体的处理器(如AuthHandler
、ValidationHandler
等)继承。
它封装了职责链的核心逻辑:
setNext()
设置下一个处理器并返回它,以便链式调用;forward()
将请求传递给下一个处理器。具体处理器类只需要关注自身的
handle()
逻辑,执行完后调用forward(request)
即可。
步骤 3:定义请求对象
class Request {public String user;public boolean isAdmin;public boolean isValid = true;public boolean isCached;// ... 字段可以根据需要扩展
}
步骤 4:创建具体的处理器
// Handler 1: 检查用户是否已登录
class AuthHandler extends BaseHandler {@Overridepublic void handle(Request request) {if (request.user == null) {System.out.println("[Auth] 用户未认证,请求终止。");// 不再向下传递return; }System.out.println("[Auth] 用户已认证。");// 交给下一个处理器forward(request); }
}// Handler 2: 管理员权限校验
class AdminHandler extends BaseHandler {@Overridepublic void handle(Request request) {if (!request.isAdmin) {System.out.println("[Admin] 普通用户,跳过管理员校验。");} else {System.out.println("[Admin] 管理员验证通过。");// 可执行管理员相关逻辑}forward(request);}
}// Handler 3: 数据清理
class SanitizeHandler extends BaseHandler {@Overridepublic void handle(Request request) {System.out.println("[Sanitize] 正在清理数据...");forward(request);}
}
步骤 5:组装责任链
public class ChainApp {public static void main(String[] args) {Request request = new Request();request.user = "哈基米";request.isAdmin = true;Handler chain = new AuthHandler();chain.setNext(new AdminHandler()).setNext(new SanitizeHandler());chain.handle(request);}
}
2.3.3 执行流程
AuthHandler
验证用户;- 验证通过后,将请求传递给
AdminHandler
; - 再传递到
SanitizeHandler
; - 如果某个处理器中断了链条,后续处理器将不会执行。
3. 优缺点分析
3.1 优势
- 处理器可复用,且易于单元测试;
- 可以在运行时动态配置责任链;
- 逻辑清晰,符合 单一职责原则(SRP);
- 处理顺序不在处理器内部硬编码,而是通过链式结构灵活定义。
3.2 缺点
- 如果链条覆盖不全,可能存在请求未被处理的情况;
- 请求在长链路中传递时,跟踪路径可能较困难;
- 动态链条的调试可能比较复杂,需要设计合适的日志记录方式。
4. 使用
4.1 常见应用场景
- 中间件管道(认证、校验、日志记录)
- GUI 事件处理(鼠标/键盘事件)
- 垃圾邮件过滤
- 文件格式识别
4.2 适用场景
当满足以下条件时,可以考虑使用职责链模式:
- 有多个对象可以处理同一个请求;
- 需要解耦请求发送方和接收方;
- 需要复用或动态调整处理逻辑顺序;
- 需要在某些条件下提前终止处理流程。
总结
**职责链模式的核心在于灵活性。**使得逻辑可以像流水线一样串联起来,每个处理器专注于一个小任务。合理的使用这种模式可以让代码逻辑更加清晰、便于测试和扩展,还能避免冗长的条件分支。