news 2026/9/30 6:41:09

责任链模式(Chain of Responsibility)实战指南:基于 java-design-patterns 仓库构建可扩展的请求处理链

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
责任链模式(Chain of Responsibility)实战指南:基于 java-design-patterns 仓库构建可扩展的请求处理链
  • 示例工程
  • 教程

【免费下载链接】java-design-patterns

Design patterns implemented in Java

项目地址:https://gitcode.com/GitHub_Trending/ja/java-design-patterns
点击查看免费下载

导读

本文以 java-design-patterns 仓库中的 chain-of-responsibility 模块为核心,系统讲解责任链(Chain of Responsibility)这一行为型设计模式:它如何将请求的发送者与接收者解耦,让多个处理对象依次获得处理机会,直到链上某个环节接手。读完本文,你将掌握责任链模式的意图、适用场景、优劣势,并能结合仓库源码理解其接口设计、优先级排序机制与测试验证方式,直接将其复用到日志过滤、中间件、审批流等真实业务中。

模式概述:责任链解决什么问题

责任链模式(Chain of Responsibility)是 Gang of Four 提出的经典行为型设计模式,别名包括Chain of Command(命令链)、Chain of Objects(对象链)、Responsibility Chain(职责链)。

它的核心意图是:解耦请求的发送者与接收者——不把请求直接绑定到某个具体处理者,而是让多个对象都有机会处理请求。这些接收对象被组织成一条链,请求沿着链向后传递,直到某个对象将其处理完毕。正如本模块入口类 App.java 的注释所概括的:责任链由一组命令对象和一系列处理对象组成,每个处理对象内部包含判断自己能否处理该类命令的逻辑,无法处理的命令则交给链中的下一个处理对象,同时提供在链尾追加新处理对象的机制。

现实世界类比:技术支持呼叫中心

设想一个技术支持呼叫中心:每一级支持人员都是链上的一个处理节点。客户来电后,请求先到达一线支持代表;如果问题简单,一线直接解决;如果问题复杂,则升级给二线技术员;若仍未解决,再继续向更高级别升级,直到有能力的专家接手。每一级都是链上的一个 handler,请求沿链逐级传递,直到找到合适的处理者——发送者无需知道具体由谁处理,这正是责任链模式在现实中的典型映射。

一句话通俗解释

责任链帮助我们构建一条对象链:请求从链的一端进入,在对象之间依次传递,直到遇到一个合适的处理者将其接住。

Wikipedia 定义

在面向对象设计中,责任链模式由命令对象的源头和一系列处理对象组成。每个处理对象包含定义它能处理哪些命令对象的逻辑,其余命令则传递给链中的下一个处理对象。

责任链的请求流转流程

下图完整呈现了请求在责任链中的流转逻辑:客户端发起请求,从 Handler 1 开始依次判断Can Handler X process it?,能处理则就地处理(Handler X processes request),不能处理则交给下一个处理者;若整条链都没有处理者能接住,则请求最终落入Request unhandled(未被处理)状态。

从这张流程图可以提炼出责任链的两个关键设计决策:

  1. 每个节点都要回答"我能否处理"——在仓库实现中对应RequestHandler.canHandleRequest(Request)。
  2. 链的终点必须考虑"无人处理"的兜底——这也是下文"优缺点"中会讨论的 catch-all handler 问题。

编程示例:兽人王国的命令链

在 java-design-patterns 的 chain-of-responsibility 模块中,责任链以一个生动的"兽人王国"场景演示:兽人国王发出洪亮的命令,最先响应的是指挥官(commander),然后是军官(officer),最后是士兵(soldier),三者构成一条责任链。

请求对象: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(); } } public enum RequestType { DEFEND_CASTLE, TORTURE_PRISONER, COLLECT_TAX }

从源码看,Request有几个值得注意的实现细节:

  • @Getter(Lombok)自动生成getRequestType()、getRequestDescription()、isHandled();
  • 构造器对requestType与requestDescription都执行Objects.requireNonNull,从源头杜绝空请求进入责任链;
  • handled字段是单向状态机:只能通过markHandled()从未处理变为已处理,不存在"取消处理"的反向操作——正如 Request.java 的注释所强调的;
  • 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)判断当前处理者能否处理该请求按RequestType匹配
getPriority()返回处理优先级,用于确定链上顺序士兵 1、指挥官 2、军官 3
handle(Request)实际处理请求,并调用markHandled()记录日志并标记已处理
name()返回处理者名称,用于日志输出如 "Orc commander"

三个具体处理者:OrcCommander / OrcOfficer / OrcSoldier

三个具体处理者均实现RequestHandler接口,代码结构完全对称。以 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与OrcSoldier的定义方式完全相同,差异仅在各自的处理类型与优先级上。综合三个类的源码,责任链的"职责分配"如下表:

处理者源码文件能处理的请求类型优先级名称
OrcSoldierOrcSoldier.javaCOLLECT_TAX1Orc soldier
OrcCommanderOrcCommander.javaDEFEND_CASTLE2Orc commander
OrcOfficerOrcOfficer.javaTORTURE_PRISONER3Orc officer

注意:这里的优先级数字并非链的传递顺序,而用于"谁能优先接管请求"的排序。读者可以对比传统责任链(后一个 handler 持有前一个的引用、逐个next传递)与本实现的差异——本示例采用"每个 handler 独立判断 + 统一按优先级挑选"的方式,这是责任链模式的一种流式变体。

链的构建与请求分发: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)); } }

逐行拆解makeRequest的流水线逻辑:

  1. buildChain()在构造器中完成链的装配,将三个处理者放入List<RequestHandler>;
  2. .stream().sorted(Comparator.comparing(RequestHandler::getPriority))按优先级升序排序(士兵 1 → 指挥官 2 → 军官 3);
  3. .filter(handler -> handler.canHandleRequest(req))依次筛出能处理该请求的处理者;
  4. .findFirst()取优先级最高且能处理的那一个;
  5. .ifPresent(handler -> handler.handle(req))执行处理——只有真正处理时才会调用req.markHandled()。

这种"排序 + 过滤 + 取首"的实现,使得新增处理者、调整处理顺序都非常廉价:只需在buildChain()中增删成员或修改getPriority()返回值即可,无需改动分发逻辑。

入口程序与运行输出

入口程序 App.java 让兽人国王连续发出三条命令:

public static void main(String[] args) { 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"

三条命令分别由链上三个不同级别的处理者接住,直观展示了"请求沿链流转、按能力匹配处理者"的效果。

类图与源码结构全景

下图是本模块的 UML 类图(PlantUML 源文件位于 chain-of-responsibility.urm.puml,渲染图如下):

从类图与源码可以归纳出本模块的完整类结构(均位于com.iluwatar.chain包):

类 / 接口角色关键成员
RequestHandler(接口)抽象处理者canHandleRequest/getPriority/handle/name
OrcCommander具体处理者处理DEFEND_CASTLE,优先级 2
OrcOfficer具体处理者处理TORTURE_PRISONER,优先级 3
OrcSoldier具体处理者处理COLLECT_TAX,优先级 1
OrcKing链构建者 / 分发器持有List<RequestHandler> handlers,makeRequest负责分发
Request请求对象requestType、requestDescription、handled状态
RequestType(枚举)请求类型DEFEND_CASTLE、TORTURE_PRISONER、COLLECT_TAX
App程序入口main方法发起三条命令

关系要点:OrcKing通过-handlers关联RequestHandler接口(面向接口编程);OrcCommander、OrcOfficer、OrcSoldier三个具体处理者都以虚线实现该接口;Request通过requestType属性关联RequestType枚举。

如何运行与验证本模块

本模块是独立的 Maven 子模块(见 chain-of-responsibility/pom.xml),继承父项目com.iluwatar:java-design-patterns,依赖 slf4j-api、logback-classic 与 junit-jupiter-engine,并在maven-assembly-plugin中指定主类为com.iluwatar.chain.App。

在仓库根目录下,可执行以下命令查看运行结果:

# 运行责任链示例主程序 ./mvnw -pl chain-of-responsibility compile exec:java -Dexec.mainClass=com.iluwatar.chain.App # 运行该模块的全部单元测试 ./mvnw -pl chain-of-responsibility test

本模块自带两组单元测试,可用于验证模式行为:

  • OrcKingTest.java:构造了覆盖全部三种RequestType的请求列表,逐个交给OrcKing.makeRequest后断言request.isHandled()为真——即所有请求最终都能被链上某个处理者接住;
  • AppTest.java:断言App.main执行过程中不抛出异常,保证示例程序可稳定运行。

何时使用责任链模式

从本模式的意图出发,当以下场景出现时应考虑使用责任链:

  • 有多个对象可能处理同一个请求,且处理者事先未知——处理者应由运行时的请求内容自动判定;
  • 希望把请求发送给多个对象中的某一个,但不想显式指定接收者——发送者与接收者完全解耦;
  • 可处理请求的对象集合需要动态指定——链的成员与顺序可以按需调整,而不影响调用方。

真实世界中的责任链应用

责任链模式在各类框架与中间件中广泛存在,典型场景包括:

  • GUI 框架中的事件冒泡(Event Bubbling):一个事件可能被 UI 组件层级中的多个层次处理;
  • 中间件框架:请求依次穿过一条由多个处理对象组成的链(如 Web 中间件管线);
  • 日志框架:消息可依次传给一系列 logger,每个 logger 以不同方式处理(如级别过滤、格式化、输出);
  • java.util.logging.Logger#log():日志记录逐级向上传递的机制即是责任链的体现;
  • javax.servlet.Filter#doFilter():Servlet 过滤器链让每个 Filter 决定放行还是拦截请求;
  • Apache Commons Chain:专门为责任链模式提供的通用实现库。

责任链模式的收益与代价

收益(Benefits)

  • 降低耦合:请求发送者无需知道最终处理请求的具体 handler;
  • 提升职责分配的灵活性:通过增删链成员或调整顺序,即可改变请求的处理方式;
  • 可设置默认兜底处理者:当链上没有具体处理者能接住请求时,可配置一个 catch-all handler 兜底,避免请求落空。

代价(Trade-Offs)

  • 调试与理解成本上升:链越长越复杂,请求的流转路径就越难一眼看清;
  • 可能产生"无人处理"的悬空请求:如果链中缺少兜底处理者,请求会在遍历整条链后依然未被处理(对照上文流程图中的Request unhandled分支);
  • 存在性能隐患:请求可能需依次穿过多个处理者才能命中正确的那个,甚至始终找不到,带来额外的遍历开销。

针对"无人处理"问题,本仓库示例通过OrcKingTest断言了三条命令全部被处理;但在更复杂的链设计中,建议显式追加一个兜底 handler(如"unknown request"处理器),或在makeRequest的ifPresent之外补充未处理分支。

与其他设计模式的关系

  • Command 命令模式:可将请求封装为对象,封装后的命令对象恰好适合沿责任链传递;
  • Composite 组合模式:责任链常与组合模式搭配使用,树的节点天然构成层次化的处理链;
  • Decorator 装饰器模式:装饰器可以像责任链一样串联,逐层包裹并增强行为——区别在于装饰器通常全部执行,而责任链通常只由一个节点处理。

参考资料与延伸阅读

本文内容以本仓库 chain-of-responsibility 模块的 README、src/main/java/com/iluwatar/chain 下的全部源码、src/test 下的测试用例以及 etc 下的类图与流程图为准。责任链模式作为经典 GoF 行为型模式,在以下经典著作中均有系统论述,可作为延伸学习材料:Design Patterns: Elements of Reusable Object-Oriented Software、Head First Design Patterns、Pattern-Oriented Software Architecture, Volume 1: A System of Patterns、Refactoring to Patterns与Pattern Languages of Program Design 3。

  • 示例工程
  • 教程

【免费下载链接】java-design-patterns

Design patterns implemented in Java

项目地址:https://gitcode.com/GitHub_Trending/ja/java-design-patterns
点击查看免费下载
上一篇:ConvNeXt模型剪枝后微调策略:3个高效恢复性能方法指南
下一篇:CANN/cannbot-skills CSV公共字段与约定

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/30 6:38:48

ST7282彩屏驱动移植实战:从解压白屏到DMA刷屏避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/30 6:37:09

AI生成的道路模块看着接上了,车辆一过却弹跳?先查这5处碰撞接缝

把 AI 生成的道路、坡道或桥梁模块导入 Unity、Unreal 后&#xff0c;车辆经过接缝时突然抬头、侧跳、短暂离地&#xff0c;甚至无故减速&#xff0c;并不一定是悬挂参数有问题。 更常见的原因是&#xff1a;相邻碰撞体之间存在缝隙或重叠&#xff0c;视觉网格与碰撞网格轮廓不…

作者头像 李华
网站建设 2026/9/30 6:37:06

齿条轨道选供应商,这三个标准帮你避坑

在工业自动化与精密机械领域&#xff0c;齿条导轨作为核心传动部件&#xff0c;其质量直接影响设备运行精度与使用寿命。然而&#xff0c;市场上供应商水平参差不齐&#xff0c;选错厂家轻则增加维护成本&#xff0c;重则导致生产线停摆。本文基于行业实践&#xff0c;梳理三个…

作者头像 李华
网站建设 2026/9/30 6:33:51

诚信为本的自动化焊接产线工厂赢得市场信赖

随着制造业向智能化迈进&#xff0c;自动化焊接产线已成为企业提质增效的重要选择。然而&#xff0c;面对供应商的各类承诺&#xff0c;不少企业发现&#xff0c;真正影响合作质量的往往是“诚信”二字。从方案设计到交付服务&#xff0c;一个工厂是否可靠&#xff0c;直接决定…

作者头像 李华
网站建设 2026/9/30 6:33:12

Jenkins 拉取 GitLab 代码:从凭据配置到部署全指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华