设计模式——Chain(责任链)设计模式

news2025/5/16 6:18:07

摘要

责任链设计模式是一种行为设计模式,通过链式调用将请求逐一传递给一系列处理器,直到某个处理器处理了请求或所有处理器都未能处理。它解耦了请求的发送者和接收者,允许动态地将请求处理职责分配给多个对象,支持请求的灵活传递或中断。

1. 责任链设计模式是什么

1.1. 责任链设计模式

责任链模式理解就是遍历某一个接口下所有的实现类中方法。责任链模式的核心思想就是通过链式调用将请求逐一传递给一系列处理器(实现了同一个接口的对象),直到某个处理器处理了请求,或者链中的所有处理器都未能处理该请求。

1.2. 责任链模式的基本工作原理:

  1. 处理者链:将多个处理者(通常是实现了相同接口的类)链接在一起,每个处理者负责处理请求的一个特定方面。
  2. 请求传递:请求会沿着责任链传递,每个处理者都会判断是否能够处理该请求,处理后返回或继续传递给下一个处理者。
  3. 终止条件:如果某个处理者能够处理该请求,链条上后续的处理者通常会被跳过,直接返回处理结果;如果没有处理者能处理,最终请求可能会失败。

1.3. 简化的责任链模式结构

可以把责任链模式理解为:

  • 链表结构:每个节点(处理器)持有对下一个节点的引用,依次处理请求。
  • 处理器接口:每个节点实现一个接口,负责具体的处理逻辑。

责任链模式(Chain of Responsibility Pattern)与 装饰器模式(Decorator Pattern)迭代器模式(Iterator Pattern) 在某些方面有相似之处,但它们的核心思想和使用场景略有不同。

特性

责任链模式

装饰器模式

目的

解耦请求的发送者和接收者,将请求交给多个处理者逐一处理

动态为对象添加功能,增强其行为

结构

请求按链式结构传递,每个处理者持有下一个处理者的引用

对原对象进行包装,增加额外功能

请求传递

请求沿着责任链传递,直到某个处理器处理请求或链终止

不传递请求,直接在原对象上增强功能

设计模式重点

请求处理的顺序和中断条件控制

通过包装原对象来动态增加功能

典型应用场景

多个处理器顺序处理请求,逐个判断并决定是否处理

动态为对象添加行为,功能扩展而不修改原对象代码

耦合性

请求发送者与接收者解耦,减少了依赖关系

可以灵活地为对象添加功能,扩展类的功能而不修改其代码

责任

每个处理者负责部分请求处理逻辑

每个装饰器负责对原对象的功能扩展

1.4. 责任链设计模式的作用

  1. 请求传递:每个处理者(链上的对象)都有一个引用指向下一个处理者。请求沿着链依次传递,直到被某个处理者处理或链结束。
  2. 解耦:发送方无需直接与处理方耦合,降低代码的复杂度。
  3. 可扩展性:可以灵活地添加、删除或修改链上的处理者,而不影响整体结构。
  4. 职责分离:每个处理者只关注自己能处理的部分,其余的请求传递给下一个处理者。

1.5. 责任链模式的优点

  1. 解耦请求与处理者:请求方不需要知道具体处理者是谁,降低耦合。
  2. 动态组合职责链:可以灵活地增加、删除或重排链中的处理节点。
  3. 代码清晰:将复杂的逻辑分解到多个处理节点中,职责单一。

1.6. 责任链模式的缺点

  1. 性能开销:当责任链过长时,可能带来性能问题。
  2. 调试困难:请求处理链条较长时,调试排查问题可能变得复杂。
  3. 不保证请求被处理:如果没有合适的处理者,可能导致请求最终未被处理。(执行链是有默认的顺序,可以借助Spring中@Order注解来实现有序)

2. 责任链模式类图实现

2.1. 责任链模式的结构

责任链模式主要包含以下角色:

  1. Handler(处理者抽象类/接口):
    • 定义处理请求的接口,并存储下一个处理者的引用。
  1. ConcreteHandler(具体处理者):
    • 继承或实现 Handler,处理它负责的请求。如果无法处理,则将请求转发给下一个处理者。
  1. Client(客户端):
    • 创建责任链,并向链头发出请求。

3. 责任链模式使用场景

3.1. 多级请求处理

  • 当一个请求需要经过多个对象处理,且不明确具体是由哪个对象处理时。
  • 示例:
    • 日志系统:根据日志级别(DEBUG、INFO、WARN、ERROR)动态决定由哪个日志处理器处理。
    • 审批流:如贷款审批流程,依次经过组长、经理、总监的审批。

3.2. 请求处理职责动态变化

  • 处理职责可能会随时增减,且希望能灵活调整处理顺序。
  • 示例:风控规则引擎:新增或调整规则处理节点时,动态维护责任链。

3.3. 避免请求发送者与接收者耦合

  • 当请求发送者不需要知道接收者的具体实现时,可以通过责任链解耦。
  • 示例:支付系统:如多种支付渠道(银行卡、第三方支付、余额支付)按优先级尝试支付。

3.4. 请求的处理逻辑可以被分解

  • 请求的处理逻辑非常复杂,且可以拆分成多个步骤时,每个步骤都可以成为责任链上的一个节点。
  • 示例:HTTP 请求拦截器:如在请求到达服务器前,依次经过身份认证、权限验证、数据校验等。

3.5. 需要支持请求的灵活传递或中断

  • 某个处理器完成后,可以继续传递给下一个处理器,也可以选择中止传递。
  • 示例:事件处理机制:如 Java 的事件监听器,事件沿着监听器链依次传递,直到某个监听器处理完成。

3.6. 责任链具体场景示例

3.6.1. 风控规则链

在信贷风控中,每个用户的申请需要经过一系列的规则检查,例如:

  • 身份证校验
  • 黑名单检测
  • 信用评分计算
  • 额度限制校验

这些规则可以按顺序依次处理,或者某个规则失败时中断处理。

3.6.2. API 请求过滤

如在微服务架构中,API 请求经过以下责任链:

  • 身份验证拦截器
  • 参数校验拦截器
  • 权限检查拦截器
  • 日志记录拦截器

3.6.3. 异常处理链

多个异常处理器按顺序处理异常,例如:

  • 数据库异常处理器
  • 网络异常处理器
  • 业务逻辑异常处理器
    责任链可以逐级定位和处理异常。

3.6.4. 广告投放系统

  • 按用户属性(年龄、性别、兴趣)动态匹配投放规则。
  • 若某规则不适用,则传递给下一个规则。

4. 责任链模式示例(Spring)

在 Spring 中使用 责任链模式 并让链上的处理器由 Spring 容器管理,可以通过以下方式实现:利用 Spring 的 @Component 注解 配合 自动注入(@Autowired) 或者通过 @Order 注解 实现责任链的顺序化管理。

4.1. 定义责任链接口

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

每个校验规则实现该接口,handle 方法返回 true 代表校验通过,false 代表校验失败。

4.2. 定义请求对象

@Data
public class Request {
    private String userId;
    private int creditScore;
    private double loanAmount;

    // 其他字段...
    // Getter & Setter
}

4.3. 创建具体的处理器

每个处理器(节点)实现 Handler 接口,例如:

4.3.1. 身份校验处理器

@Component
public class IdentityValidationHandler implements Handler {

    @Override
    public boolean handle(Request request) {
        // 假设用户 ID 不能为空
        if (request.getUserId() == null || request.getUserId().isEmpty()) {
            System.out.println("身份校验失败");
            return false;
        }
        System.out.println("身份校验通过");
        return true;
    }
}

4.3.2. 黑名单检测处理器

@Component
public class BlacklistCheckHandler implements Handler {

    @Override
    public boolean handle(Request request) {
        // 假设用户 "123" 是黑名单用户
        if ("123".equals(request.getUserId())) {
            System.out.println("黑名单校验失败");
            return false;
        }
        System.out.println("黑名单校验通过");
        return true;
    }
}

4.3.3. 信用评分校验处理器

public class CreditScoreCheckHandler implements Handler {

    @Override
    public boolean handle(Request request) {
        // 假设信用评分不能低于 600
        if (request.getCreditScore() < 600) {
            System.out.println("信用评分校验失败");
            return false;
        }
        System.out.println("信用评分校验通过");
        return true;
    }
}

4.3.4. 额度检查处理器

@Component
public class LoanAmountCheckHandler implements Handler {

    @Override
    public boolean handle(Request request) {
        // 假设贷款金额不能超过 50 万
        if (request.getLoanAmount() > 500000) {
            System.out.println("额度检查失败");
            return false;
        }
        System.out.println("额度检查通过");
        return true;
    }
}

4.4. 责任链测试

package com.zhuangxiaoyan.hyxftest.chain;

import com.zhuangxiaoyan.hyxftest.HyxfTestApplication;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

/**
 * chaintest
 *
 * @author xjl
 * @version 2024/12/04 19:56
 **/

@SpringBootTest(classes = HyxfTestApplication.class)
public class ChainTest {

    @Autowired
    private HandlerChain handlerChain;


    @Test
    public void chainTest() {
        handlerChain.process(new Request("122222", 500, 10000));
    }
}

5. 博文参考

《软件设计模式》

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SQLite:DDL(数据定义语言)的基本用法

SQLite&#xff1a;DDL&#xff08;数据定义语言&#xff09;的基本用法 1 主要内容说明2 相关内容说明2.1 创建表格&#xff08;create table&#xff09;2.1.1 SQLite常见的数据类型2.1.1.1 integer&#xff08;整型&#xff09;2.1.1.2 text&#xff08;文本型&#xff09;2…

【Elasticsearch】实现分布式系统日志高效追踪

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/literature?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;…

WEB安全 PHP学习

PHP基础 PHP编码显示问题 header ("Content-type: text/html; charsetgb2312"); header("Content-Type: text/html;charsetutf-8"); windows需要使用gbk编码显示 源码是 <?php header ("Content-type: text/html; charsetgb2312"); sys…

MySQL 单表练习

DQL练习1-学生表 创建如下学生表 create table student( id int, name varchar(20), gender varchar(20), chinese int, math int, english int ); insert into student values (1,张明,男,89,78,90), (2,李进,男…

详解Java数据库编程之JDBC

目录 首先创建一个Java项目 在Maven中央仓库下载mysql connector的jar包 针对MySQL版本5 针对MySQL版本8 下载之后&#xff0c;在IDEA中创建的项目中建立一个lib目录&#xff0c;然后把刚刚下载好的jar包拷贝进去&#xff0c;然后右键刚刚添加的jar包&#xff0c;点击‘添…

第32天:安全开发-JavaEE应用Servlet路由技术JDBCMybatis数据库生命周期

时间轴&#xff1a; 32天主要学习内容&#xff1a; 1、JavaEE-HTTP-Servlet技术 2、JavaEE-数据库-JDBC&Mybatis java技术使用历史&#xff08;2023 &#xff09;&#xff1a; JavaEE-HTTP-Servlet&路由&周期: java学习范围&#xff1a; 3、Java: 功能:数据…

【大数据技术基础】 课程 第3章 Hadoop的安装和使用 大数据基础编程、实验和案例教程(第2版)

第3章 Hadoop的安装和使用 3.1 Hadoop简介 Hadoop是Apache软件基金会旗下的一个开源分布式计算平台&#xff0c;为用户提供了系统底层细节透明的分布式基础架构。Hadoop是基于Java语言开发的&#xff0c;具有很好的跨平台特性&#xff0c;并且可以部署在廉价的计算机集群中。H…

VTK中矩阵vtkMatrix4x4类的介绍和使用

1、矩阵-齐次坐标介绍 常见的点一般是Pt&#xff08;X,Y,Z&#xff09;&#xff0c;相当于一个13矩阵&#xff0c;而矩阵相乘的话一般是第一个矩阵的列数要等于第二个矩阵的行数。此处需要引入齐次坐标的概念&#xff1a;从广义上讲&#xff0c;齐次坐标就是用n1维向量表示n 维…

RoGS: Large Scale Road Surface Reconstruction based on 2D Gaussian Splatting

RoGS 摘要简介RoGS基于高斯面元的道路表面表示(Road Surface Representation Based on Guassian Surfel)2D Gaussian Surfel:Road Surface Modeling:Why use 2D Gaussian Surfels? 基于轨迹的初始化&#xff08;Trajectory-base Initialization&#xff09;优化&#xff08;Op…

使用 postman 传递 binary 类型的图片到后端接口遇到的坑

使用 psotman 传 binary 类型图片报错&#xff1a; -2024-12-04 [http-nio-9090-exec-1] WARN org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver Resolved [org.springframework.http.converter.HttpMessageNotReadableException: Required r…

微服务通讯系统(2)

软件设计及核心代码展示 数据库表设计&#xff0c;ES搜索表设计&#xff0c;Redis键值对设计 数据库表设计 &#xff08;1&#xff09;用户表设计 这里的ID是指的是在系统中用户是第几个注册的&#xff08;从1开始&#xff09; user_id是指用户的唯一ID是通过uuid()函数生成…

修复docker启动失败:Failed to start Docker Application Container Engine

配置了镜像源之后&#xff0c;运行sudo systemctl restart docker.service失败&#xff0c;提示让运行systemctl status docker.service或journalctl -xeu docker.service查看详细信息。 运行后者发现有如下日志&#xff1a; 红色区域是我设置的一个镜像源这个日志的意思就是…

神经网络入门实战:(十四)pytorch 官网内置的 CIFAR10 数据集,及其网络模型

(一) pytorch 官网内置的网络模型 图像处理&#xff1a; Models and pre-trained weights — Torchvision 0.20 documentation (二) CIFAR10数据集的分类网络模型&#xff08;仅前向传播&#xff09;&#xff1a; 下方的网络模型图片有误&#xff0c;已做修改&#xff0c;具…

微信小程序wx.showShareMenu配置全局分享功能

在app.js文件中配置如下即可&#xff1a; onLaunch() {//开启分享功能this.overShare()},/*** 开启朋友圈分享功能* 监听路由切换/自动执行*/overShare() {wx.onAppRoute((res) > {// console.log(route, res)let pages getCurrentPages()let view pages[pages.length - …

Java刷题常见的集合类,各种函数的使用以及常见的类型转化等等

前言 相信大家在刷算法题的过程中&#xff0c;好不容易想出来大概的思路&#xff0c;也知道去用哪个集合类&#xff0c;但各个集合类的一些命令都长得太像&#xff0c;很容易将他们弄错&#xff0c;并且在各集合之间的转化也是特别烦人&#xff0c;还有很多实用的函数都知道可…

用 NotePad++ 运行 Java 程序

安装包 网盘链接 下载得到的安装包: 安装步骤 双击安装包开始安装. 安装完成: 配置编码 用 NotePad 写 Java 程序时, 需要设置编码. 在 设置, 首选项, 新建 中进行设置, 可以对每一个新建的文件起作用. 之前写的文件不起作用. 在文件名处右键, 可以快速打开 CMD 窗口, 且路…

【金猿CIO展】复旦大学附属中山医院计算机网络中心副主任张俊钦:推进数据安全风险评估,防范化解数据安全风险,筑牢医疗数据安全防线...

‍ 张俊钦 本文由复旦大学附属中山医院计算机网络中心副主任张俊钦撰写并投递参与“数据猿年度金猿策划活动——2024大数据产业年度优秀CIO榜单及奖项”评选。 大数据产业创新服务媒体 ——聚焦数据 改变商业 数据要素时代&#xff0c;医疗数据已成为医院运营与决策的重要基石…

计算机视觉——相机标定(Camera Calibration)

文章目录 1. 简介2. 原理3. 相机模型3.1 四大坐标系3.2 坐标系间的转换关系3.2.1 世界坐标系到相机坐标系3.2.2 相机坐标系到图像坐标系3.2.3 像素坐标系转换为图像坐标系3.2.4 世界坐标转换为像素坐标 3.3 畸变3.3.1 畸变类型3.3.1.1 径向畸变&#xff08;Radial Distortion&a…

Go学习:编译器(编写程序时应该注意的点)

一、注意&#xff1a; LiteIDE工具&#xff1a; &#xff08;1&#xff09;创建项目后&#xff0c;同一个目录下的go文件 只能有一个 main函数&#xff0c;如果多个文件都有main函数&#xff0c;会出现编译错误。例如&#xff1a; &#xff08;2&#xff09;如果一个目录下多…

【计算机网络】实验9: 路由信息协议RIP

实验9 路由信息协议RIP 一、实验目的 本实验的主要目的是深入理解RIP&#xff08;路由信息协议&#xff09;的工作原理&#xff0c;以便掌握其在网络中的应用。通过对RIP的学习&#xff0c;我们将探讨该协议如何实现路由选择和信息传播&#xff0c;从而确保数据包能够在网络中…