- 示例工程
- 教程
【免费下载链接】java-design-patterns
Design patterns implemented in Java
导读
责任链(Chain of Responsibility)是 Gang of Four 提出的经典行为型设计模式之一,其核心价值在于将请求的发送者与接收者解耦:允许多个对象依次获得处理请求的机会,请求沿着链条传递,直到被某个合适的处理器接收。本文以 java-design-patterns 仓库中chain-of-responsibility模块(源码位于 chain-of-responsibility/src/main/java/com/iluwatar/chain)为蓝本,结合官方阿拉伯语文档 localization/ar/chain-of-responsibility/README.md 的核心内容,从模式意图、代码实现、类图结构、适用场景到优缺点与关联模式,带你完整掌握责任链模式在 Java 中的落地写法。读完本文,你将能够独立实现一套可动态调整处理顺序、按需增加处理器的请求分发机制。
模式概览:责任链是什么
其他名称
责任链模式在不同资料中也被称为:
- 命令链(Chain of Command)
- 对象链(Chain of Objects)
- 责任链(Responsibility Chain)
模式意图
责任链模式通过给予多个对象处理请求的机会,将请求发送者与其接收者解耦。接收对象被串联成一条链,请求沿着链传递,直到某个对象将其处理完毕。发送者无需知道"谁"会处理请求,只需发出请求即可。
现实世界类比
文档给出了一个生动的例子:兽人国王(Orc King)大声向军队下达命令,离他最近的、依次响应的是指挥官(Commander)、军官(Officer)与士兵(Soldier)。指挥官、军官、士兵共同构成了一条"责任链"——每条命令沿链传递,直到找到能处理它的那个人。
用简单的话说:该模式帮助构建一条对象链,请求从一端进入,从一个对象流转到另一个对象,直到遇到合适的处理器为止。
权威定义
在面向对象设计中,责任链模式由一组命令对象源和一系列处理对象组成。每个处理对象都包含定义其可处理命令类型的逻辑;无法处理的请求则被传递给链中的下一个处理对象。
程序化示例:兽人军团的请求处理链
本仓库中chain-of-responsibility模块用"兽人国王下达命令、军团分级响应"的故事演示了该模式,共包含 8 个 Java 文件。下面按职责逐一拆解。
第一步:请求载体Request与RequestType
请求对象封装了被传递的信息,其源码见 Request.java:
@Getter public class Request { private final RequestType requestType; private final String requestDescription; private boolean handled; public Request(final RequestType requestType, final String requestDescription) { this.requestType = Objects.requireNonNull(requestType); this.requestDescription = Objects.requireNonNull(requestDescription); } public void markHandled() { this.handled = true; } @Override public String toString() { return getRequestDescription(); } }要点说明:
requestType:请求类型,链上每个处理器通过它判断自己"是否能够处理"该请求;requestDescription:请求描述文本,同时作为toString()的返回值,便于日志输出;handled与markHandled():记录请求是否已被处理。源码注释明确说明该状态只能从"未处理"单向变为"已处理",不存在"撤销处理"的路径;- 构造器通过
Objects.requireNonNull强制要求类型与描述非空,避免空指针隐患。
请求类型定义在 RequestType.java:
public enum RequestType { DEFEND_CASTLE, TORTURE_PRISONER, COLLECT_TAX }本示例只有三种请求:保卫城堡、拷问囚犯、征收赋税。
第二步:处理器抽象RequestHandler
所有处理器实现统一接口 RequestHandler.java,接口定义了四个方法:
public interface RequestHandler { boolean canHandleRequest(Request req); int getPriority(); void handle(Request req); String name(); }| 方法 | 职责 |
|---|---|
canHandleRequest(Request) | 判断当前处理器是否有能力处理该请求 |
getPriority() | 返回处理优先级(用于决定链中调用顺序) |
handle(Request) | 实际执行处理逻辑 |
name() | 返回处理器名称,用于日志输出 |
第三步:三个具体处理器
指挥官 OrcCommander.java 只处理"保卫城堡"类请求:
@Slf4j public class OrcCommander implements RequestHandler { @Override public boolean canHandleRequest(Request req) { return req.getRequestType() == RequestType.DEFEND_CASTLE; } @Override public int getPriority() { return 2; } @Override public void handle(Request req) { req.markHandled(); LOGGER.info("{} handling request \"{}\"", name(), req); } @Override public String name() { return "Orc commander"; } }军官 OrcOfficer.java 与士兵 OrcSoldier.java 的定义方式与指挥官完全一致,只是处理对象和元数据不同,三者配置对比如下:
| 处理器 | 可处理的请求类型 | 优先级 | 名称 |
|---|---|---|---|
OrcCommander | DEFEND_CASTLE | 2 | Orc commander |
OrcOfficer | TORTURE_PRISONER | 3 | Orc officer |
OrcSoldier | COLLECT_TAX | 1 | Orc soldier |
三个类均通过 Lombok 的@Slf4j注解获得LOGGER,并在handle()中先调用req.markHandled()标记请求已处理,再输出日志。
第四步:组装链条OrcKing
国王 OrcKing.java 负责构建链条并发起请求:
public class OrcKing { private List<RequestHandler> handlers; public OrcKing() { buildChain(); } private void buildChain() { handlers = Arrays.asList(new OrcCommander(), new OrcOfficer(), new OrcSoldier()); } public void makeRequest(Request req) { handlers .stream() .sorted(Comparator.comparing(RequestHandler::getPriority)) .filter(handler -> handler.canHandleRequest(req)) .findFirst() .ifPresent(handler -> handler.handle(req)); } }请求分发逻辑可以拆解为四步流水线:
buildChain():将三个处理器放入List<RequestHandler>完成链条组装;sorted(...):按getPriority()升序排列,确保处理顺序可控;filter(...):过滤出所有"有能力处理该请求"的处理器;findFirst().ifPresent(...):取第一个符合条件的处理器执行handle()。
需要特别指出的是:从源码结构看,本实现并未采用经典责任链"每个处理器持有下一个处理器引用"的链表式写法,而是由OrcKing统一持有处理器列表,通过"优先级排序 + 能力过滤 + 取首个"的方式完成请求路由,达到的效果与责任链一致——发送者不知道也不关心最终由谁处理。
第五步:运行演示与输出
程序入口 App.java 演示了完整调用:
var king = new OrcKing(); king.makeRequest(new Request(RequestType.DEFEND_CASTLE, "defend castle")); king.makeRequest(new Request(RequestType.TORTURE_PRISONER, "torture prisoner")); king.makeRequest(new Request(RequestType.COLLECT_TAX, "collect tax"));控制台输出:
Orc commander handling request "defend castle" Orc officer handling request "torture prisoner" Orc soldier handling request "collect tax"三种不同类型的请求分别被链条中对应的处理器接收,全程无需在调用方指定接收者。
类图结构
OrcKing.java 持有RequestHandler列表,三个具体处理器实现RequestHandler接口,Request依赖RequestType,整体结构如下图所示(图片来源 chain-of-responsibility/etc/chain-of-responsibility.urm.png,其 PlantUML 源文件见 chain-of-responsibility/etc/chain-of-responsibility.urm.puml):
责任链模式类图:OrcKing 持有 RequestHandler 列表,OrcCommander、OrcOfficer、OrcSoldier 实现 RequestHandler 接口
源码级实现细节与测试验证
测试用例印证"请求必被处理"
模块配套的单元测试 OrcKingTest.java 覆盖了所有请求类型,断言国王发出的每一个请求最终都被标记为已处理:
class OrcKingTest { private static final List<Request> REQUESTS = List.of( new Request(RequestType.DEFEND_CASTLE, "Don't let the barbarians enter my castle!!"), new Request(RequestType.TORTURE_PRISONER, "Don't just stand there, tickle him!"), new Request(RequestType.COLLECT_TAX, "Don't steal, the King hates competition ...")); @Test void testMakeRequest() { final var king = new OrcKing(); REQUESTS.forEach(request -> { king.makeRequest(request); assertTrue(request.isHandled(), "Expected all requests from King to be handled, but [" + request + "] was not!"); }); } }该测试验证了责任链的核心承诺:只要链条中存在能处理该请求的处理器,请求就不会丢失,且测试中的三类请求恰好与三个处理器一一对应。
模式在仓库中的定位
- 模块分类:Behavioral(行为型)模式;
- 核心标签:Gang of Four、Decoupling(解耦)、Event-driven、Messaging;
- 模块完整英文文档见 chain-of-responsibility/README.md,阿拉伯语译文即本文所依托的 localization/ar/chain-of-responsibility/README.md。
何时使用责任链模式
根据官方文档,当出现以下情况时应考虑使用责任链模式:
- 多个对象都可能处理同一个请求,且处理器无法预先确定——应让系统自动判定由谁处理;
- 希望向若干对象中的某一个发出请求,但不想显式指定接收者;
- 能够处理请求的对象集合需要动态确定(例如运行时增删处理器或调整顺序)。
反之,如果链条很长且结构复杂,导致流程难以追踪,或需要严格保证每个请求都有处理结果,则需要谨慎评估是否引入"兜底处理器"。
现实世界中的典型应用
责任链模式在主流框架中随处可见,文档列举了以下典型场景:
- GUI 框架中的事件冒泡:一个事件可能在 UI 组件层级的多层中被处理;
- 中间件框架:请求依次穿过一串处理对象;
- 日志框架:消息可经过一串 Logger,每个 Logger 以不同方式处理;
java.util.logging.Logger#log():日志记录按级别沿 logger 层级向上传递;- Apache Commons Chain:基于责任链思想的通用链式处理框架;
javax.servlet.Filter#doFilter():Servlet 过滤器链,每个 Filter 处理后调用chain.doFilter()将请求传给下一个,是责任链模式的教科书级应用。
优点与代价
优点
- 降低耦合:请求发送者无需知道具体由哪个处理器处理请求;
- 职责分配更灵活:可以通过改变链条成员与排列顺序,增删或调整对请求的处理责任;
- 支持默认处理器:当链中没有具体处理器能处理请求时,可以设置一个兜底的默认处理器。
代价
- 调试与理解成本高:当链条又长又复杂时,追踪请求的流转路径会比较困难;
- 请求可能无人处理:如果链条中没有"全捕获"(catch-all)处理器,请求可能最终处于未处理状态;
- 存在性能开销:请求可能经过多个处理器才能命中目标,甚至遍历整条链仍找不到处理器。
与其他模式的关系
责任链模式常与其他行为型/结构型模式配合使用,本仓库中对应的模式模块可作为对照学习材料:
- 命令模式:可将请求封装为对象,再沿责任链传递;
- 组合模式:责任链常与组合模式结合使用(例如 UI 组件树与事件冒泡);
- 装饰器模式:装饰器可以像责任链中的职责一样串联使用。
参考资料
官方文档引用的经典著作如下,可作为深入学习的延伸阅读:
- Design Patterns: Elements of Reusable Object-Oriented Software(GoF 四人组原著)
- Head First Design Patterns
- Pattern-Oriented Software Architecture, Volume 1: A System of Patterns
- Refactoring to Patterns
- Pattern Languages of Program Design 3
小结
通过本仓库chain-of-responsibility模块,我们完整走通了责任链模式的"意图 → 建模 → 实现 → 测试 → 应用"全链路:Request封装请求状态,RequestHandler统一处理器契约,OrcCommander/OrcOfficer/OrcSoldier各司其职,OrcKing负责组装链条并按优先级路由请求。这套结构清晰、可测试、可动态调整的实现,可直接迁移到中间件、过滤器、事件分发等真实业务场景中。
- 示例工程
- 教程
【免费下载链接】java-design-patterns
Design patterns implemented in Java
相关推荐
责任链模式(Chain of Responsibility)实战指南:基于 java-design-patterns 仓库构建可扩展的请求处理链
责任链模式(Chain of Responsibility)实战指南:基于 java design patterns 仓库构建可扩展的请求处理链 导读 本文以
示例工程教程责任链模式(Chain of Responsibility)实战解析:Java 设计模式中的请求处理链
责任链模式(Chain of Responsibility)实战解析:Java 设计模式中的请求处理链 责任链(Chain of Responsibility)
示例工程教程Fluent Interface 模式实战:用 Java 方法链构建可读的流式 API(java-design-patterns 仓库解析)
Fluent Interface 模式实战:用 Java 方法链构建可读的流式 API(java design patterns 仓库解析) Fluent In
示例工程教程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考