设计模式(十四)行为型:职责链模式详解

职责链模式(Chain of Responsibility Pattern)是 GoF 23 种设计模式中的行为型模式之一,其核心价值在于将多个处理对象(处理器)连接成一条链,使请求沿着链传递,直到被某个处理器处理为止。它解耦了请求的发送者与接收者,允许动态地组织处理流程,提升系统的灵活性与可扩展性。职责链模式是实现“开闭原则”的典范,广泛应用于审批流程(请假、报销)、事件处理(GUI 事件分发)、日志系统(多级日志处理器)、中间件管道(如 Web 框架的过滤器链)、异常处理、权限校验等需要多级判断或顺序处理的场景,是构建可配置、可插拔业务流程的关键架构模式。

一、详细介绍

职责链模式解决的是“一个请求可能由多个对象处理,但具体由谁处理在运行时决定”的问题。在传统设计中,客户端需要显式判断由哪个对象处理请求,导致代码中充斥条件判断(if-else 或 switch),难以维护和扩展。当处理逻辑变更或新增处理器时,客户端代码必须修改。

职责链模式通过将多个处理器组织成一条链,客户端只需将请求发送给链的首节点,后续传递由处理器自行决定。每个处理器都持有对下一个处理器的引用(或通过外部容器管理),并在处理请求时:

  1. 判断自己是否能处理该请求;
  2. 若能处理,则执行业务逻辑并结束;
  3. 若不能处理,则将请求转发给链中的下一个处理器;
  4. 若链尾仍未处理,则可选择丢弃或抛出异常。

该模式包含以下核心角色:

  • Handler(抽象处理器):定义处理请求的接口,通常包含一个方法(如 handleRequest())和一个指向后继处理器的引用(successor)。可以是抽象类或接口。
  • ConcreteHandler(具体处理器):实现 Handler 接口,包含具体的处理逻辑。它决定是否处理当前请求,若不处理则将请求转发给后继。
  • Client(客户端):创建处理器链,并向链的首节点发送请求。客户端不关心具体由哪个处理器处理,也不依赖具体处理器类型。

职责链的组织方式有两种:

  1. 显式链(Explicit Chain):每个处理器持有对下一个处理器的引用,形成链式结构。
  2. 中心化链(Centralized Chain):由一个容器(如 Chain 类)管理处理器列表,按顺序调用。

职责链模式的关键优势:

  • 解耦请求发送者与接收者:客户端无需知道具体处理者。
  • 增强系统灵活性:可动态添加、删除或重排处理器。
  • 符合开闭原则:新增处理器无需修改现有代码。
  • 支持多种处理逻辑:可实现“首个匹配即处理”或“全部处理”等策略。

与“状态模式”相比,职责链关注请求的传递与处理,状态模式关注对象行为随状态改变;与“观察者模式”相比,职责链是单向链式传递,观察者是广播式通知;与“策略模式”相比,职责链允许多个策略顺序参与,策略模式是单一策略选择

二、职责链模式的UML表示

以下是职责链模式的标准 UML 类图:

implements
implements
implements
successor
sends 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; }@Overridepublic 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");}@Overridepublic 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");}@Overridepublic 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");}@Overridepublic 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图(简化版)
extends
extends
extends
successor
sends request
creates
LeaveRequest
-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 定义了处理链的结构和接口。
  • TeamLeaderDepartmentManagerGeneralManager 实现具体审批逻辑。
  • 请求从 teamLeader 开始,根据天数逐级传递。
  • 客户端只需将请求交给链首,无需关心处理细节。

四、总结

特性说明
核心目的解耦请求发送者与接收者,实现请求的动态处理
实现机制构建处理器链,请求沿链传递直至被处理
优点降低耦合、增强灵活性、支持动态配置、符合开闭原则
缺点请求可能未被处理(需设计默认处理)、性能可能下降(链过长)、调试困难
适用场景审批流程、事件处理、日志分级、过滤器链、异常处理、权限校验
不适用场景处理逻辑简单、必须由特定对象处理、性能敏感且链过长

职责链模式使用建议

  • 应确保链中有至少一个处理器能处理请求,避免“黑洞”。
  • 可引入“默认处理器”处理未匹配请求。
  • 支持运行时动态构建和修改链结构。
  • 在 Java 中,Servlet 的 FilterChain、Spring 的拦截器链是典型应用。

架构师洞见:
职责链模式是“流程可配置化”与“关注点分离”的高级体现。在现代架构中,其思想已演变为中间件管道事件驱动架构工作流引擎的核心。例如,在 Web 框架(如 Express、Koa)中,中间件链按顺序处理 HTTP 请求;在微服务中,API 网关的过滤器链执行认证、限流、日志等操作;在工作流引擎(如 Activiti)中,任务节点构成处理链;在前端框架中,事件冒泡机制本质是职责链。

未来趋势是:职责链将与低代码流程引擎深度融合,通过可视化拖拽构建处理链;在AI Agent 系统中,Agent 的“决策链”(Reasoning Chain)可视为职责链,每个步骤由不同工具或模型处理;在可观测性系统中,日志、指标、追踪数据将通过职责链进行分级处理与路由。

掌握职责链模式,有助于设计出灵活、可配置、易扩展的业务流程。作为架构师,应在涉及“多级判断”、“顺序处理”或“流程编排”的场景中主动引入职责链。职责链不仅是模式,更是流程治理的哲学——它提醒我们:真正的灵活性,来自于将“决策路径”从硬编码中解放出来,交由配置与链式逻辑动态决定。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/916498.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/916498.shtml
英文地址,请注明出处:http://en.pswp.cn/news/916498.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

WAIC 2025 热点解读:如何构建 AI 时代的“视频神经中枢”?

一、&#x1f310; WAIC 2025 大会看点&#xff1a;AI 正在“长出眼睛与身体” 在 2025 年的人工智能大会&#xff08;WAIC 2025&#xff09;上&#xff0c;“大模型退幕后&#xff0c;具身智能登场”成为最具共识的趋势转向。从展区到主论坛&#xff0c;再到各大企业发布的新…

OpenCV+Python

安装 OpenCV&#xff1a; Python&#xff1a;直接 pip install opencv-python&#xff08;核心库&#xff09;和 opencv-contrib-python&#xff08;扩展功能&#xff09;。 pip install opencv-python pip install opencv-contrib-python 验证安装&#xff1a; import cv2…

现代C++的一般编程规范

一般情况下不要使用std::endl&#xff0c;尤其是在循环中&#xff0c;因为可能一开始你只是想要打印一个换行符&#xff0c;但是"endl"做的更多&#xff0c;其还会刷新缓冲区&#xff0c;这会额外花费很多时间&#xff0c;相反&#xff0c;只需要使用“\n"&…

38.安卓逆向2-frida hook技术-过firda检测(三)(通过SO文件过检测原理)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a;图灵Python学院 工具下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1bb8NhJc9eTuLzQr39lF55Q?pwdzy89 提取码&#xff1…

创建属于自己的github Page主页

安装手册 安装手册 环境要求 Node.js version 18.0 安装 Node.js 时&#xff0c;建议勾选所有和依赖相关的选项。 安装步骤 安装 Docusaurus 最简单的方法是使用 create-docusaurus 命令行工具&#xff0c;它可以帮助你快速搭建一个 Docusaurus 网站的基础框架。 你可以在…

Unity Catalog与Apache Iceberg如何重塑Data+AI时代的企业数据架构

在2025年DataAI Summit上&#xff0c;Databricks发布了一系列重大更新&#xff0c;标志着企业数据治理进入新阶段。其中&#xff0c;Unity Catalog的增强功能和对Apache Iceberg的全面支持尤为引人注目。这些更新不仅强化了跨平台数据管理能力&#xff0c;还推动了开放数据生态…

雨季,汽车经常跑山区,该如何保养?

雨季来临&#xff0c;山区道路变得湿滑难行&#xff0c;频繁穿梭于此的汽车面临着前所未有的挑战。如何在这样恶劣的环境中确保爱车安然无恙&#xff1f;本文将为你详细解析雨季经常跑山区的汽车该如何保养&#xff0c;让你在遭遇突发状况时也能从容应对。当雨季遇上山区路况&a…

Spring Boot音乐服务器项目-查询音乐模块

一、项目架构概览 该音乐播放服务器采用经典的MVC分层架构&#xff0c;核心模块包括&#xff1a; 实体层&#xff1a;定义数据模型Mapper层&#xff1a;数据库操作接口Controller层&#xff1a;HTTP请求处理工具层&#xff1a;加密、响应封装等辅助功能 项目核心功能包括用户…

Imagine:高效免费的图片压缩工具

很多时候&#xff0c;我们需要对图片进行压缩&#xff0c;却苦于找不到免费又好用的工具。这里给大家推荐一款电脑端的图片压缩软件——Imagine。 Imagine文末获取 它有诸多优点&#xff1a; 开源免费&#xff1a;无需担心付费问题&#xff0c;完全免费使用。 便捷易用&#…

《Uniapp-Vue 3-TS 实战开发》自定义年月日时分秒picker组件

目前组件: 组件完整代码: <template><view><picker mode="multiSelector" :value="multiIndex" :range="multiRange" @change="onMultiChange"><view class="picker">{{ formattedDateTime }}&l…

生命通道的智慧向导:Deepoc具身智能如何重塑医院导诊机器人的“仁心慧眼”

生命通道的智慧向导&#xff1a;Deepoc具身智能如何重塑医院导诊机器人的“仁心慧眼”清晨八点的三甲医院门诊大厅&#xff0c;一台导诊机器人突然转向无障碍通道。视觉系统捕捉到轮椅上的颤抖双手&#xff0c;自动降低语速并调大屏幕字体&#xff1b;识别出老人病历本上的“心…

【51单片机和数码管仿真显示问题共阴共阳代码】2022-9-24

缘由单片机和数码管仿真显示问题-嵌入式-CSDN问答 #include "REG52.h" unsigned char code smgduan[]{0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f ,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0,64,15,56}; //共阴0~F消隐减号 void smxs(unsigned char mz, unsigned c…

Java#包管理器来时的路

不依赖任何Jar包 - HelloWorld.java mkdir demo && cd demo;# HelloWorld.java cat > HelloWorld.java << EOF public class HelloWorld {public static void main(String[] args) {System.out.println("Hello, world!");} } EOF# 编译class javac …

Android Framework知识点

1 重点知识 1.1 Alarm 当手机重启或者应用被杀死的时候&#xff0c;Alarm会被删除&#xff0c;因此&#xff0c;如果想通过Alarm来完成长久定时任务是不可靠的&#xff0c;如果非要完成长久定时任务&#xff0c;可以这样&#xff1a;将应用的所有Alarm信息存到数据库中&#xf…

代码随想录算法训练营Day6 | 哈希表 Part 1

一、今日学习目标 掌握哈希表的核心理论&#xff08;哈希函数、哈希碰撞及解决方法&#xff09;&#xff0c;理解数组、set、map 三种哈希结构的适用场景&#xff0c;并通过「两个数组的交集」「快乐数」「两数之和」三道题目&#xff0c;实战掌握哈希表在快速查找、去重、键值…

5.13.树、森林与二叉树的转换

当使用"孩子兄弟表示法"存储树或森林时&#xff0c;最终会呈现出与二叉树类似的形态&#xff0c;所以树、森林与二叉树之间的转换本质上就是画出采用孩子兄弟表示法存储的树和森林。一."树->二叉树"的转换&#xff1a;1.例一&#xff1a;以上述图片左边…

Spring 核心流程

Spring 核心流程前言一、AbstractApplicationContext#refresh 方法解析1.1 前置1.2 refresh 方法1.2.1 prepareRefresh1.2.2 obtainFreshBeanFactory1.2.3 prepareBeanFactory1.2.4 postProcessBeanFactory1.2.5 invokeBeanFactoryPostProcessors1.2.6 registerBeanPostProcess…

RS485转Profinet网关与JRT激光测距传感器在S7-1200 PLC系统中的技术解析与应用

RS485转Profinet网关与JRT激光测距传感器在S7-1200 PLC系统中的技术解析与应用技术核心&#xff1a;协议转换与数据桥梁在工业自动化系统中&#xff0c;RS485转Profinet网关承担着协议翻译官的角色。以XD-MDPN100型号为例&#xff0c;其本质是将RS485设备的串口数据封装为Profi…

《C++ string 完全指南:string的模拟实现》

string的模拟实现 文章目录string的模拟实现一、浅拷贝和深拷贝1.浅拷贝2.深拷贝3.写时拷贝二、定义string的成员变量三、string的接口实现1.string的默认成员函数&#xff08;1&#xff09;构造函数实现&#xff08;2&#xff09;析构函数实现&#xff08;3&#xff09;拷贝构…

造成服务器内存不足的原因有什么

服务器在日常的运行过程中&#xff0c;会存储大量关于企业重要的数据信息&#xff0c;偶尔会出现内存飙升空间不足的情况&#xff0c;服务器内存作为服务器数据处理和存储的主要空间&#xff0c;异常占用会导致服务器性能降低&#xff0c;影响到企业业务的响应速度&#xff0c;…