设计模式(十四)行为型:职责链模式详解
设计模式(十四)行为型:职责链模式详解
职责链模式(Chain of Responsibility Pattern)是 GoF 23 种设计模式中的行为型模式之一,其核心价值在于将多个处理对象(处理器)连接成一条链,使请求沿着链传递,直到被某个处理器处理为止。它解耦了请求的发送者与接收者,允许动态地组织处理流程,提升系统的灵活性与可扩展性。职责链模式是实现“开闭原则”的典范,广泛应用于审批流程(请假、报销)、事件处理(GUI 事件分发)、日志系统(多级日志处理器)、中间件管道(如 Web 框架的过滤器链)、异常处理、权限校验等需要多级判断或顺序处理的场景,是构建可配置、可插拔业务流程的关键架构模式。
一、详细介绍
职责链模式解决的是“一个请求可能由多个对象处理,但具体由谁处理在运行时决定”的问题。在传统设计中,客户端需要显式判断由哪个对象处理请求,导致代码中充斥条件判断(if-else 或 switch),难以维护和扩展。当处理逻辑变更或新增处理器时,客户端代码必须修改。
职责链模式通过将多个处理器组织成一条链,客户端只需将请求发送给链的首节点,后续传递由处理器自行决定。每个处理器都持有对下一个处理器的引用(或通过外部容器管理),并在处理请求时:
- 判断自己是否能处理该请求;
- 若能处理,则执行业务逻辑并结束;
- 若不能处理,则将请求转发给链中的下一个处理器;
- 若链尾仍未处理,则可选择丢弃或抛出异常。
该模式包含以下核心角色:
- Handler(抽象处理器):定义处理请求的接口,通常包含一个方法(如
handleRequest())和一个指向后继处理器的引用(successor)。可以是抽象类或接口。 - ConcreteHandler(具体处理器):实现
Handler接口,包含具体的处理逻辑。它决定是否处理当前请求,若不处理则将请求转发给后继。 - Client(客户端):创建处理器链,并向链的首节点发送请求。客户端不关心具体由哪个处理器处理,也不依赖具体处理器类型。
职责链的组织方式有两种:
- 显式链(Explicit Chain):每个处理器持有对下一个处理器的引用,形成链式结构。
- 中心化链(Centralized Chain):由一个容器(如
Chain类)管理处理器列表,按顺序调用。
职责链模式的关键优势:
- 解耦请求发送者与接收者:客户端无需知道具体处理者。
- 增强系统灵活性:可动态添加、删除或重排处理器。
- 符合开闭原则:新增处理器无需修改现有代码。
- 支持多种处理逻辑:可实现“首个匹配即处理”或“全部处理”等策略。
与“状态模式”相比,职责链关注请求的传递与处理,状态模式关注对象行为随状态改变;与“观察者模式”相比,职责链是单向链式传递,观察者是广播式通知;与“策略模式”相比,职责链允许多个策略顺序参与,策略模式是单一策略选择。
二、职责链模式的UML表示
以下是职责链模式的标准 UML 类图:
#mermaid-svg-lGhouPTup90mOwdj {font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-lGhouPTup90mOwdj .error-icon{fill:#552222;}#mermaid-svg-lGhouPTup90mOwdj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-lGhouPTup90mOwdj .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-lGhouPTup90mOwdj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-lGhouPTup90mOwdj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-lGhouPTup90mOwdj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-lGhouPTup90mOwdj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-lGhouPTup90mOwdj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-lGhouPTup90mOwdj .marker.cross{stroke:#333333;}#mermaid-svg-lGhouPTup90mOwdj svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-lGhouPTup90mOwdj g.classGroup text{fill:#9370DB;fill:#131300;stroke:none;font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-lGhouPTup90mOwdj g.classGroup text .title{font-weight:bolder;}#mermaid-svg-lGhouPTup90mOwdj .nodeLabel,#mermaid-svg-lGhouPTup90mOwdj .edgeLabel{color:#131300;}#mermaid-svg-lGhouPTup90mOwdj .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-lGhouPTup90mOwdj .label text{fill:#131300;}#mermaid-svg-lGhouPTup90mOwdj .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-lGhouPTup90mOwdj .classTitle{font-weight:bolder;}#mermaid-svg-lGhouPTup90mOwdj .node rect,#mermaid-svg-lGhouPTup90mOwdj .node circle,#mermaid-svg-lGhouPTup90mOwdj .node ellipse,#mermaid-svg-lGhouPTup90mOwdj .node polygon,#mermaid-svg-lGhouPTup90mOwdj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-lGhouPTup90mOwdj .divider{stroke:#9370DB;stroke:1;}#mermaid-svg-lGhouPTup90mOwdj g.clickable{cursor:pointer;}#mermaid-svg-lGhouPTup90mOwdj g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-lGhouPTup90mOwdj g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-lGhouPTup90mOwdj .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-lGhouPTup90mOwdj .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-lGhouPTup90mOwdj .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-lGhouPTup90mOwdj .dashed-line{stroke-dasharray:3;}#mermaid-svg-lGhouPTup90mOwdj #compositionStart,#mermaid-svg-lGhouPTup90mOwdj .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-lGhouPTup90mOwdj #compositionEnd,#mermaid-svg-lGhouPTup90mOwdj .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-lGhouPTup90mOwdj #dependencyStart,#mermaid-svg-lGhouPTup90mOwdj .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-lGhouPTup90mOwdj #dependencyStart,#mermaid-svg-lGhouPTup90mOwdj .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-lGhouPTup90mOwdj #extensionStart,#mermaid-svg-lGhouPTup90mOwdj .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-lGhouPTup90mOwdj #extensionEnd,#mermaid-svg-lGhouPTup90mOwdj .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-lGhouPTup90mOwdj #aggregationStart,#mermaid-svg-lGhouPTup90mOwdj .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-lGhouPTup90mOwdj #aggregationEnd,#mermaid-svg-lGhouPTup90mOwdj .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-lGhouPTup90mOwdj .edgeTerminals{font-size:11px;}#mermaid-svg-lGhouPTup90mOwdj :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;}implementsimplementsimplementssuccessorsends request«abstract»Handler-successor: Handler+setSuccessor(successor: Handler)+handleRequest(request: Request)ConcreteHandlerA+handleRequest(request: Request)ConcreteHandlerB+handleRequest(request: Request)ConcreteHandlerC+handleRequest(request: Request)Client+main(args: String[])
图解说明:
Handler定义处理接口和后继引用。ConcreteHandlerA/B/C实现具体处理逻辑,可选择处理请求或转发。- 处理器通过
setSuccessor()构成链。 - 客户端向链首发送请求,请求沿链传递。
三、一个简单的Java程序实例及其UML图
以下是一个公司请假审批系统的示例,展示不同级别的管理者对不同天数的请假请求进行审批。
Java 程序实例
// 请求类class LeaveRequest { private String employee; private int days; private String reason; public LeaveRequest(String employee, int days, String reason) { this.employee = employee; this.days = days; this.reason = reason; } // Getter 方法 public String getEmployee() { return employee; } public int getDays() { return days; } public String getReason() { return reason; } @Override public String toString() { return \"LeaveRequest{\" + \"employee=\'\" + employee + \'\\\'\' + \", days=\" + days + \", reason=\'\" + reason + \'\\\'\' + \'}\'; }}// 抽象处理器:审批者abstract class Approver { protected Approver successor; protected String name; protected String position; public Approver(String name, String position) { this.name = name; this.position = position; } public void setSuccessor(Approver successor) { this.successor = successor; } // 处理请求的抽象方法 public abstract void handleRequest(LeaveRequest request);}// 具体处理器:组长(可批1-3天)class TeamLeader extends Approver { public TeamLeader(String name) { super(name, \"Team Leader\"); } @Override public void handleRequest(LeaveRequest request) { if (request.getDays() <= 3) { System.out.println(\"✅ [\" + position + \" \" + name + \"] 批准了 \" + request.getEmployee() + \" 的 \" + request.getDays() + \" 天请假申请。\"); } else if (successor != null) { System.out.println(\"⏭️ [\" + position + \" \" + name + \"] 无法处理,转交上级...\"); successor.handleRequest(request); } }}// 具体处理器:部门经理(可批4-7天)class DepartmentManager extends Approver { public DepartmentManager(String name) { super(name, \"Department Manager\"); } @Override public void handleRequest(LeaveRequest request) { if (request.getDays() <= 7) { System.out.println(\"✅ [\" + position + \" \" + name + \"] 批准了 \" + request.getEmployee() + \" 的 \" + request.getDays() + \" 天请假申请。\"); } else if (successor != null) { System.out.println(\"⏭️ [\" + position + \" \" + name + \"] 无法处理,转交上级...\"); successor.handleRequest(request); } }}// 具体处理器:总经理(可批任意天数)class GeneralManager extends Approver { public GeneralManager(String name) { super(name, \"General Manager\"); } @Override public void handleRequest(LeaveRequest request) { System.out.println(\"✅ [\" + position + \" \" + name + \"] 特批了 \" + request.getEmployee() + \" 的 \" + request.getDays() + \" 天请假申请。\"); }}// 客户端使用示例public class ChainOfResponsibilityDemo { public static void main(String[] args) { System.out.println(\"🏢 公司请假审批系统 - 职责链模式示例\\n\"); // 创建处理器链 Approver teamLeader = new TeamLeader(\"张组长\"); Approver deptManager = new DepartmentManager(\"李经理\"); Approver gm = new GeneralManager(\"王总\"); // 构建链:组长 -> 部门经理 -> 总经理 teamLeader.setSuccessor(deptManager); deptManager.setSuccessor(gm); // 模拟不同请假请求 LeaveRequest req1 = new LeaveRequest(\"小明\", 2, \"感冒\"); LeaveRequest req2 = new LeaveRequest(\"小红\", 5, \"家庭事务\"); LeaveRequest req3 = new LeaveRequest(\"小刚\", 10, \"出国旅游\"); System.out.println(\"📝 处理请假请求:\"); teamLeader.handleRequest(req1); System.out.println(\"---\"); teamLeader.handleRequest(req2); System.out.println(\"---\"); teamLeader.handleRequest(req3); System.out.println(\"\\n💡 说明:请求沿链传递,直到被合适处理器处理。\"); System.out.println(\"🔧 可动态调整链结构,如新增总监层。\"); }}
实例对应的UML图(简化版)
#mermaid-svg-6UuCl6hSh36kdA9l {font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-6UuCl6hSh36kdA9l .error-icon{fill:#552222;}#mermaid-svg-6UuCl6hSh36kdA9l .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6UuCl6hSh36kdA9l .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-6UuCl6hSh36kdA9l .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6UuCl6hSh36kdA9l .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6UuCl6hSh36kdA9l .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6UuCl6hSh36kdA9l .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6UuCl6hSh36kdA9l .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6UuCl6hSh36kdA9l .marker.cross{stroke:#333333;}#mermaid-svg-6UuCl6hSh36kdA9l svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6UuCl6hSh36kdA9l g.classGroup text{fill:#9370DB;fill:#131300;stroke:none;font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-6UuCl6hSh36kdA9l g.classGroup text .title{font-weight:bolder;}#mermaid-svg-6UuCl6hSh36kdA9l .nodeLabel,#mermaid-svg-6UuCl6hSh36kdA9l .edgeLabel{color:#131300;}#mermaid-svg-6UuCl6hSh36kdA9l .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-6UuCl6hSh36kdA9l .label text{fill:#131300;}#mermaid-svg-6UuCl6hSh36kdA9l .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-6UuCl6hSh36kdA9l .classTitle{font-weight:bolder;}#mermaid-svg-6UuCl6hSh36kdA9l .node rect,#mermaid-svg-6UuCl6hSh36kdA9l .node circle,#mermaid-svg-6UuCl6hSh36kdA9l .node ellipse,#mermaid-svg-6UuCl6hSh36kdA9l .node polygon,#mermaid-svg-6UuCl6hSh36kdA9l .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-6UuCl6hSh36kdA9l .divider{stroke:#9370DB;stroke:1;}#mermaid-svg-6UuCl6hSh36kdA9l g.clickable{cursor:pointer;}#mermaid-svg-6UuCl6hSh36kdA9l g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-6UuCl6hSh36kdA9l g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-6UuCl6hSh36kdA9l .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-6UuCl6hSh36kdA9l .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-6UuCl6hSh36kdA9l .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-6UuCl6hSh36kdA9l .dashed-line{stroke-dasharray:3;}#mermaid-svg-6UuCl6hSh36kdA9l #compositionStart,#mermaid-svg-6UuCl6hSh36kdA9l .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-6UuCl6hSh36kdA9l #compositionEnd,#mermaid-svg-6UuCl6hSh36kdA9l .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-6UuCl6hSh36kdA9l #dependencyStart,#mermaid-svg-6UuCl6hSh36kdA9l .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-6UuCl6hSh36kdA9l #dependencyStart,#mermaid-svg-6UuCl6hSh36kdA9l .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-6UuCl6hSh36kdA9l #extensionStart,#mermaid-svg-6UuCl6hSh36kdA9l .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-6UuCl6hSh36kdA9l #extensionEnd,#mermaid-svg-6UuCl6hSh36kdA9l .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-6UuCl6hSh36kdA9l #aggregationStart,#mermaid-svg-6UuCl6hSh36kdA9l .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-6UuCl6hSh36kdA9l #aggregationEnd,#mermaid-svg-6UuCl6hSh36kdA9l .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-6UuCl6hSh36kdA9l .edgeTerminals{font-size:11px;}#mermaid-svg-6UuCl6hSh36kdA9l :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;}extendsextendsextendssuccessorsends requestcreatesLeaveRequest-employee: String-days: int-reason: String+getEmployee()+getDays()+getReason()«abstract»Approver-successor: Approver-name: String-position: String+setSuccessor(successor: Approver)+handleRequest(request: LeaveRequest)TeamLeader+handleRequest(request: LeaveRequest)DepartmentManager+handleRequest(request: LeaveRequest)GeneralManager+handleRequest(request: LeaveRequest)Client+main(args: String[])
运行说明:
Approver定义了处理链的结构和接口。TeamLeader、DepartmentManager、GeneralManager实现具体审批逻辑。- 请求从
teamLeader开始,根据天数逐级传递。 - 客户端只需将请求交给链首,无需关心处理细节。
四、总结
职责链模式使用建议:
- 应确保链中有至少一个处理器能处理请求,避免“黑洞”。
- 可引入“默认处理器”处理未匹配请求。
- 支持运行时动态构建和修改链结构。
- 在 Java 中,Servlet 的
FilterChain、Spring 的拦截器链是典型应用。
架构师洞见:
职责链模式是“流程可配置化”与“关注点分离”的高级体现。在现代架构中,其思想已演变为中间件管道、事件驱动架构和工作流引擎的核心。例如,在 Web 框架(如 Express、Koa)中,中间件链按顺序处理 HTTP 请求;在微服务中,API 网关的过滤器链执行认证、限流、日志等操作;在工作流引擎(如 Activiti)中,任务节点构成处理链;在前端框架中,事件冒泡机制本质是职责链。未来趋势是:职责链将与低代码流程引擎深度融合,通过可视化拖拽构建处理链;在AI Agent 系统中,Agent 的“决策链”(Reasoning Chain)可视为职责链,每个步骤由不同工具或模型处理;在可观测性系统中,日志、指标、追踪数据将通过职责链进行分级处理与路由。
掌握职责链模式,有助于设计出灵活、可配置、易扩展的业务流程。作为架构师,应在涉及“多级判断”、“顺序处理”或“流程编排”的场景中主动引入职责链。职责链不仅是模式,更是流程治理的哲学——它提醒我们:真正的灵活性,来自于将“决策路径”从硬编码中解放出来,交由配置与链式逻辑动态决定。


