设计模式——责任链设计模式(行为型)

article/2025/8/11 12:56:47

摘要

责任链设计模式是一种行为型设计模式,旨在将请求的发送者与接收者解耦,通过多个处理器对象按链式结构依次处理请求,直到某个处理器处理为止。它包含抽象处理者、具体处理者和客户端等核心角色。该模式适用于多个对象可能处理请求的场景,如风控系统中的贷款申请流程,通过链式组合处理整个流程。实现方式包括链式引用和集合遍历等,适用于信贷申请风控校验等实际应用。

1. 责任链设计模式定义

责任链设计模式(Chain of Responsibility Pattern)是一种行为型设计模式,允许多个处理器对象都有机会处理请求,避免请求的发送者与接收者之间的耦合关系。这些处理器按照链式结构连接,每个处理器决定是否处理请求,或将其传递给链上的下一个处理器。

1.1.1. ✅ 责任链设计模式定义要点:

项目

内容

目的

将请求的发送者与接收者解耦,让多个对象有机会处理请求。

结构

每个处理者持有对下一个处理者的引用,形成链式结构。

核心机制

请求沿链传递,直到被某个处理者处理为止。

关键点

不关心请求由谁处理,处理器职责明确,链条可灵活组合。

1.1.2. ✅ UML 类图核心角色:

  • Handler(抽象处理者):定义处理请求的接口,维护下一个处理者引用。
  • ConcreteHandler(具体处理者):实现处理逻辑,判断是否处理请求,否则传递。
  • Client(客户端):构建责任链并发起请求。

1.1.3. ✅ 示例场景说明(以风控为例):

风控系统中,一条贷款申请请求需要依次经过:

黑名单检查 → 信用评分检查 → 欺诈风险检查 → 额度判断

每个检查环节都是一个责任节点,最终链式组合处理整个申请流程。

2. 责任链设计模式结构

2.1. 责任链设计模式类图

  1. 处理者 (Handler) 声明了所有具体处理者的通用接口。
  2. 基础处理者 (Base Handler) 是一个可选的类, 你可以将所有处理者共用的样本代码放置在其中。
  3. 具体处理者 (Concrete Handlers) 包含处理请求的实际代码。 每个处理者接收到请求后, 都必须决定是否进行处理, 以及是否沿着链传递请求。
  4. 客户端 (Client) 可根据程序逻辑一次性或者动态地生成链。 值得注意的是, 请求可发送给链上的任意一个处理者, 而非必须是第一个处理者。

3. 责任链设计模式实现方式

责任链设计模式的实现方式有两种主要形式:链式引用方式集合遍历方式。在 Java 和 Spring 项目中,两种方式都可以灵活使用,以下是详细实现说明:

3.1. ✅ 链式引用方式(经典责任链)

3.1.1. 定义抽象处理器

public abstract class RiskHandler {protected RiskHandler next;public void setNext(RiskHandler next) {this.next = next;}public void handle(Request request) {if (doHandle(request) && next != null) {next.handle(request);}}protected abstract boolean doHandle(Request request);
}

3.1.2. 实现具体处理器

@Component
public class BlacklistHandler extends RiskHandler {@Overrideprotected boolean doHandle(Request request) {System.out.println("黑名单校验");// 模拟通过return true;}
}@Component
public class CreditScoreHandler extends RiskHandler {@Overrideprotected boolean doHandle(Request request) {System.out.println("信用评分校验");return true;}
}

3.1.3. 构造责任链(可由 Spring 初始化)

@Component
public class RiskChainBuilder {@Autowiredprivate BlacklistHandler blacklistHandler;@Autowiredprivate CreditScoreHandler creditScoreHandler;@PostConstructpublic void init() {blacklistHandler.setNext(creditScoreHandler);// 可继续设置下一个}public RiskHandler getChain() {return blacklistHandler;}
}

3.2. ✅ 集合遍历方式(策略+责任链)

这种方式更适合使用 Spring 管理 Bean。

3.2.1. 定义统一接口

public interface RiskHandler {boolean handle(Request request);
}

3.2.2. 实现多个处理器(用注解自动注入)

@Component
public class BlacklistHandler implements RiskHandler {public boolean handle(Request request) {System.out.println("黑名单处理");return true;}
}@Component
public class CreditHandler implements RiskHandler {public boolean handle(Request request) {System.out.println("信用处理");return true;}
}

3.2.3. 责任链执行器

@Component
public class RiskChainExecutor {@Autowiredprivate List<RiskHandler> handlers;public void process(Request request) {for (RiskHandler handler : handlers) {if (!handler.handle(request)) {break; // 拦截处理}}}
}

3.3. ✅ 责任链实现方式总结

项目

链式引用方式

集合遍历方式

Spring友好

较差,需要手动串联

非常友好,自动注入 List

动态配置顺序

不方便(写死在代码)

可通过 @Order、配置排序

灵活性

灵活,可动态组合链

更适合顺序一致、处理流程清晰

推荐程度

简单链条推荐使用

企业级风控强烈推荐

4. 责任链设计模式适合场景

4.1. ✅ 适合使用责任链设计模式的场景

场景

说明

多个对象可以处理同一请求

请求的处理者不唯一,多个处理器具备处理能力。

处理逻辑具有先后顺序且可拆分

如风控流程、审批流程、日志处理、事件流转等。

希望动态添加或修改处理逻辑链

支持运行时动态组合处理器链条,提高灵活性。

解耦请求与处理器之间的关系

请求发送者无需知道谁会处理,只需交给链处理。

处理节点对请求是否继续传递有决策权

允许部分处理节点终止请求,如认证失败即停止。

4.2. ❌ 不适合使用责任链设计模式的场景

场景

原因

处理流程固定,结构简单

使用责任链会增加结构复杂度,得不偿失。

必须同时执行所有处理逻辑,不允许中断

责任链是短路模型(遇到失败可中断),不适合要求“全部执行”的场景。

处理者之间高度耦合或依赖上下文共享

处理节点应独立,依赖过多会降低可维护性。

对性能要求极高、链条很长

每次请求都需遍历链条,可能带来额外性能损耗。

请求必须同时由多个处理器并行处理

责任链是串行模式,不适合并行处理场景。

4.3. 📌 实际应用建议

使用责任链的条件

建议

节点解耦 + 处理器独立 + 顺序可变

✅ 可考虑使用

所有处理器必须全执行,顺序固定

❌ 不推荐,改用责任集(全执行)或责任调度中心

所有逻辑都写死在 if-else 中

✅ 可用责任链重构,提高扩展性和可维护性

5. 责任链设计模式实战示例

5.1. ✅ 应用场景:信贷申请风控校验

系统在用户提交借款申请时,需要依次执行以下校验逻辑(处理链):

  • 黑名单校验
  • 实名认证校验
  • 信用评分校验

5.2. 🧩 请求对象

public class LoanRequest {private String userId;private String name;private int age;// 其他信息// getter/setter省略
}

5.3. 🧩 风控处理器接口

public interface RiskHandler {/*** 返回 true 表示通过校验,继续执行后续处理器;false 表示终止处理。*/boolean handle(LoanRequest request);
}

5.4. 🧩 风控处理器实现(举例4个)

5.4.1. 🔹 黑名单校验器

@Component
@Order(1)
public class BlacklistHandler implements RiskHandler {@Overridepublic boolean handle(LoanRequest request) {System.out.println("黑名单校验: " + request.getUserId());// 假设用户不在黑名单中return true;}
}

5.4.2. 🔹 实名认证校验器

@Component
@Order(2)
public class RealNameHandler implements RiskHandler {@Overridepublic boolean handle(LoanRequest request) {System.out.println("实名认证校验: " + request.getName());return true;}
}

5.4.3. 🔹 信用评分校验器

@Component
@Order(3)
public class CreditScoreHandler implements RiskHandler {@Overridepublic boolean handle(LoanRequest request) {System.out.println("信用评分校验: " + request.getUserId());return true;}
}

5.4.4. 🔹 反欺诈规则校验器

@Component
@Order(4)
public class AntiFraudHandler implements RiskHandler {@Overridepublic boolean handle(LoanRequest request) {System.out.println("反欺诈校验: " + request.getUserId());return true;}
}

5.5. 🧩 风控责任链执行器(接口变实现遍历)

@Component
public class RiskCheckExecutor {@Autowiredprivate List<RiskHandler> handlers;public boolean execute(LoanRequest request) {for (RiskHandler handler : handlers) {if (!handler.handle(request)) {System.out.println("风控拦截,终止流程");return false;}}System.out.println("所有风控校验通过");return true;}
}

@Order 确保处理器执行顺序一致。

5.6. 🧩 测试调用示例(如用于 Controller)

@RestController
@RequestMapping("/loan")
public class LoanController {@Autowiredprivate RiskCheckExecutor executor;@PostMapping("/apply")public ResponseEntity<String> apply(@RequestBody LoanRequest request) {boolean pass = executor.execute(request);if (pass) {return ResponseEntity.ok("风控通过,进入审批");} else {return ResponseEntity.status(HttpStatus.FORBIDDEN).body("风控未通过");}}
}

5.7. ✅ 项目示例总结

特性

说明

注解注入

使用 @Component 注册处理器,@Autowired注入集合

避免构造函数

所有注入均为字段注入,无需构造器方式

解耦结构

每个处理器职责单一,互不干扰

可扩展性

增加校验逻辑仅需新增实现类并加上 @Component@Order

6. 责任链设计模式思考

博文参考

  • https://github.com/guanguans/design-patterns-for-humans-cn

http://www.hkcw.cn/article/hygXUSKMkH.shtml

相关文章

软件的兼容性如何思考与分析?

软件功能的兼容性是指软件在实现功能的时候&#xff0c;能够与其他软件、硬件、系统环境以及数据格式等相互协作、互不冲突&#xff0c;并且能够正确处理不同来源或不同版本的数据、接口和功能模块的能力。它确保软件在多种环境下能够正常运行&#xff0c;同时与其他系统和用户…

C++ —— STL容器——string类

1. 前言 本篇博客将会介绍 string 中的一些常用的函数&#xff0c;在使用 string 中的函数时&#xff0c;需要加上头文件 string。 2. string 中的常见成员函数 2.1 初始化函数 string 类中的常用的初始化函数有以下几种&#xff1a; 1. string() …

DFS每日刷题

目录 P1605 迷宫 P1451 求细胞数量 P1219 [USACO1.5] 八皇后 Checker Challenge P1605 迷宫 #include <iostream> using namespace std; int n, m, t; int a[20][20]; int startx, starty, endx, endy; bool vis[20][20]; int res; int dx[] {0, 1, 0, -1}; int dy[]…

USART 串口通信全解析:原理、结构与代码实战

文章目录 USARTUSART简介USART框图USART基本结构数据帧起始位侦测数据采样波特率发生器串口发送数据 主要代码串口接收数据与发送数据主要代码 USART USART简介 一、USART 的全称与基本定义 英文全称 USART&#xff1a;Universal Synchronous Asynchronous Receiver Transmi…

C# winform 教程(一)

一、安装方法 官网下载社区免费版&#xff0c;在线下载安装 VS2022官网下载地址 下载后双击启动&#xff0c;选择需要模块&#xff08;net桌面开发&#xff0c;通用window平台开发&#xff0c;或者其他自己想使用的模块&#xff0c;后期可以修改&#xff09;&#xff0c;选择…

ZLG ZCANPro,ECU刷新,bug分享

文章目录 摘要 📋问题的起因bug分享 ✨思考&反思 🤔摘要 📋 ZCANPro想必大家都不陌生,买ZLG的CAN卡,必须要用的上位机软件。在汽车行业中,有ECU软件升级的需求,通常都通过UDS协议实现程序的更新,满足UDS升级的上位机要么自己开发,要么用CANoe或者VFlash,最近…

Matlab作图之 subplot

1. subplot(m, n, p) 将当前图形划分为m*n的网格&#xff0c;在 p 指定的位置创建坐标轴 matlab 按照行号对子图的位置进行编号 第一个子图是第一行第一列&#xff0c;第二个子图是第二行第二列......... 如果指定 p 位置存在坐标轴&#xff0c; 此命令会将已存在的坐标轴设…

【STM32F1标准库】理论——外部中断

目录 一、中断介绍 二、外部引脚EXTI申请的中断 三、外部中断的适用场景 四、其他注意事项 一、中断介绍 STM32可以触发中断的外设有外部引脚(EXTI)、定时器、ADC、DMA、串口、I2C、SPI等 中断同一由NVIC管理 n表示一个外设可能同时占用多个中断通道 优先级的值越小优先…

SAP学习笔记 - 开发18 - 前端Fiori开发 应用描述符(manifest.json)的用途

上一章讲了 Component配置&#xff08;组件化&#xff09;。 本章继续讲Fiori的知识。 目录 1&#xff0c;应用描述符(Descriptor for Applications) 1&#xff09;&#xff0c; manifest.json 2&#xff09;&#xff0c;index.html 3&#xff09;&#xff0c;Component.…

定时任务:springboot集成xxl-job-core(一)

springboot:2.7.2 xxl-job-core: 2.3.0 一、集成xxl-job 1. 在gitee上下载xxl-job项目 git clone https://gitee.com/xuxueli0323/xxl-job.git 2. 执行以下目录下的sql /xxl-job-2.3.0/doc/db/tables_xxl_job.sql 3. 在xxl-job-admin的项目中配置数据库信息 ### xxl-job, data…

【STM32开发板】接口部分

一、USB接口 可以看到USBP和USBN与PA12,PA11引脚相接,根据协议&#xff0c;需要添加上拉电阻 二、ADC和DAC 根据原理图找到可以作为ADC和DAC的引脚 ADC和DAC属于模拟部分的&#xff0c;所以要接模拟地 三、指示灯电路 找几个通用的引脚&#xff0c;因为单片机的灌电流比拉电流…

阻塞队列BlockingQueue解析

阻塞队列是一个支持两个附加操作的队列。这两个附加的操作支持阻塞的插入和移除的方法。 阻塞插入&#xff1a;当队列满的时候&#xff0c;队列会阻塞插入元素的线程&#xff0c;直到队列不满。 阻塞移除&#xff1a;当队列空的时候&#xff0c;队列会阻塞移除元素的线程&…

[Redis] Redis命令在Pycharm中的使用

初次学习&#xff0c;如有错误还请指正 目录 String命令 Hash命令 List命令 set命令 SortedSet命令 连接pycharm的过程见&#xff1a;[Redis] 在Linux中安装Redis并连接桌面客户端或Pycharm-CSDN博客 redis命令的使用见&#xff1a;[Redis] Redis命令&#xff08;1&#xf…

车载控制器的“机电一体化”深度集成

我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 所谓鸡汤&#xff0c;要么蛊惑你认命&#xff0c;要么怂恿你拼命&#xff0c;但都是回避问题的根源&…

PINN模型相关原理

PINN模型相关原理 目录 PINN模型相关原理原本的物理界的利用神经网络的参数估计PINN 的原理介绍一、基本思想二、PINN 的损失函数三、自动微分&#xff08;Autodiff&#xff09;四、PINN 的优势与挑战 原本的物理界的利用神经网络的参数估计 原本物理界需要确定一个三维流体&a…

计算机基础——宏病毒防御与网络技术

文章目录 宏病毒详解与防范措施宏病毒简介宏病毒的特点宏病毒的传播途径宏病毒的防范措施宏病毒的检测与清除 自治计算机与自治系统解析什么是自治计算机&#xff1f;技术特点 自治系统&#xff08;Autonomous System, AS&#xff09;特点&#xff1a;自治系统类型 总结&#x…

MySql(十一)

目录 准备工作 1&#xff09;准备一张表 2&#xff09;插入数据 分组 1&#xff09;通过性别去统计各组的平局工资 2.limit关键字 不使用limit的关键字 使用limit的关键字 使用limit关键字获取从指定行开始获取 准备工作 1&#xff09;准备一张表 CREATE table role(roleid INT…

论文阅读(六)Open Set Video HOI detection from Action-centric Chain-of-Look Prompting

论文来源&#xff1a;ICCV&#xff08;2023&#xff09; 项目地址&#xff1a;https://github.com/southnx/ACoLP 1.研究背景与问题 开放集场景下的泛化性&#xff1a;传统 HOI 检测假设训练集包含所有测试类别&#xff0c;但现实中存在大量未见过的 HOI 类别&#xff08;如…

使用 SASS 与 CSS Grid 实现鼠标悬停动态布局变换效果

最终效果概述 页面为 3x3 的彩色格子网格&#xff1b;当鼠标悬停任意格子&#xff0c;所在的行和列被放大&#xff1b;使用纯 CSS 实现&#xff0c;无需 JavaScript&#xff1b;利用 SASS 的模块能力大幅减少冗余代码。 HTML 结构 我们使用非常基础的结构&#xff0c;9 个 .i…

linux 后记

Linux Server 下载一个Server的版本&#xff0c;就是那种只有命令行的 学会这个就可以去租一个aliyun服务器&#xff0c;挺便宜的 如果在aliyun买服务器的话就不用管镜像源 但是如果是自己的虚拟机就必须设置镜像源&#xff0c;上网搜索阿里的镜像源&#xff0c;然后手动输入&…