设计模式 - 责任链

一、前言

​ 相信大家平时或多或少都间接接触过责任链设计模式,只是可能有些同学自己不知道此处用的是该设计模式,比如说 Java Web 中的 Filter 过滤器,就是非常经典的责任链设计模式的例子。

那么什么是责任链设计模式呢?

客户端发出一个请求,链上的对象都有机会来处理这一请求,而客户端不需要知道谁是具体的处理对象。多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。

​ 技术领域的相关定义总是那样的晦涩难懂,因此不理解不重要,下面将会用例子来帮助理解。

责任链模式有哪些优点,能解决什么问题,我们为什么要使用它呢?

优点:

  • 将请求与处理解耦。
  • 请求处理对象只需关注自己需要处理的请求进行处理即可,对于不需要自己处理的请求,直接转发给下一个处理对象即可,符合单一职责原则。
  • 具备链式传递处理请求功能,请求发送者无需知晓链路结构,只需等待请求处理结果。
  • 链路结构灵活,可以通过改变链路结构动态的新增或者删除处理对象。即易于拓展新的请求处理类,符合开闭原则。

缺点:

  • 会存在责任链太长,而大多数处理者都不会对请求进行处理的情况,导致走完整个责任链的时间太长,影响整体性能。
  • 如果责任链配置不完善,会存在处理对象循环引用,从而造成死循环,导致系统崩溃的情况。

适用场景:

  • 多条件流程判断,如权限控制
  • ERP 系统流程审批
  • Java Web 过滤器的底层实现 Filter
  • Mybatis 中的分页插件 PageHelper

二、代码示例

1. 导包信息

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.16</version>
</dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.12</version>
</dependency>

2. 代码结构

在这里插入图片描述

3. 具体代码

流程扩展类(主要记录每个流程的上一个处理对象和下一个处理对象的信息) ProcessDTO.java:

package com.dxc.responsibility.dto;import lombok.AllArgsConstructor;
import lombok.Data;/*** 流程扩展类** @Author xincheng.du* @Date 2023/8/30 14:26*/
@Data
@AllArgsConstructor
public class ProcessDTO {/*** 流程处理器id*/private Integer handlerId;/*** 全限定名*/private String fullName;/*** 上一个流程处理器id*/private Integer preHandlerId;/*** 下一个流程处理器id*/private Integer nextHandlerId;}

流程链枚举 ProcessChainEnum.java

package com.dxc.responsibility.enums;import com.dxc.responsibility.dto.ProcessDTO;
import lombok.AllArgsConstructor;/*** 流程链枚举* 此处的枚举类可以换成数据库配置,当然你也可以理解为数据库表中的一条条数据* 这样就可以根据更改枚举类或数据库中的顺序,来更改实际处理流程中的执行顺序了** @Author xincheng.du* @Date 2023/8/30 14:26*/
@AllArgsConstructor
public enum ProcessChainEnum {/*** 流程链中的第一个流程*/FIRST(new ProcessDTO(1, "com.dxc.responsibility.handler.impl.FirstProcessHandler", null, 2)),/*** 流程链中的第二个流程*/SECOND(new ProcessDTO(2, "com.dxc.responsibility.handler.impl.SecondProcessHandler", 1, 3)),/*** 流程链中的第三个流程*/THIRD(new ProcessDTO(3, "com.dxc.responsibility.handler.impl.ThirdProcessHandler", 2, null)),;ProcessDTO processDTO;public ProcessDTO getProcessDTO() {return processDTO;}}

流程处理器工厂(主要用来初始化流程链,并返回第一个流程处理器) ProcessHandlerFactory.java

package com.dxc.responsibility.factory;import cn.hutool.core.util.ReflectUtil;
import com.dxc.responsibility.dto.ProcessDTO;
import com.dxc.responsibility.handler.AbstractProcessHandler;
import com.dxc.responsibility.handler.impl.FirstProcessHandler;
import com.dxc.responsibility.service.ProcessService;
import com.dxc.responsibility.service.impl.ProcessServiceImpl;
import lombok.extern.slf4j.Slf4j;/*** 流程处理器工厂** @Author xincheng.du* @Date 2023/8/30 14:26*/
@Slf4j
public class ProcessHandlerFactory {private ProcessHandlerFactory() {}private static final ProcessService processService = new ProcessServiceImpl();/*** 初始化流程链,并返回流程链中第一个流程处理器** @return  {@link FirstProcessHandler}*/public static FirstProcessHandler getFirstProcessHandler() {// 获取第一个流程扩展类ProcessDTO firstProcessDTO = processService.getFirstProcessDTO();// 根据流程扩展类获取第一个流程处理器AbstractProcessHandler firstProcessHandler = newProcessHandler(firstProcessDTO);ProcessDTO tempProcessDTO = firstProcessDTO;Integer nextHandlerId;AbstractProcessHandler tempProcessHandler = firstProcessHandler;// 迭代遍历所有handler,以及将它们链接起来while ((nextHandlerId = tempProcessDTO.getNextHandlerId()) != null) {// 根据处理器id获取流程扩展类ProcessDTO processDTO = processService.getProcessEntity(nextHandlerId);// 根据流程扩展类获取具体的流程处理器AbstractProcessHandler processHandler = newProcessHandler(processDTO);assert tempProcessHandler != null;tempProcessHandler.setNext(processHandler);tempProcessHandler = processHandler;tempProcessDTO = processDTO;}// 返回第一个handlerreturn (FirstProcessHandler) firstProcessHandler;}/*** 根据流程扩展类获取具体的流程处理器** @param dto   流程扩展类* @return  {@link AbstractProcessHandler}*/private static AbstractProcessHandler newProcessHandler(ProcessDTO dto) {// 获取全限定类名String className = dto.getFullName();try {// 根据全限定类名,加载并初始化该类Class<?> clazz = Class.forName(className);return (AbstractProcessHandler) ReflectUtil.newInstance(clazz);} catch (ClassNotFoundException e) {log.error("根据流程扩展类获取流程处理器失败,原因:{},流程处理器:{}", e.getMessage(), dto.getFullName());e.printStackTrace();}return null;}}

抽象流程处理器(主要是用来定义每个具体处理的方法模板,不做具体逻辑处理,具体的流程处理器需要集成当前抽象类,并实现各自的流程处理逻辑) AbstractProcessHandler.java

package com.dxc.responsibility.handler;/*** 流程处理抽象类** @Author xincheng.du* @Date 2023/8/31 11:25*/
public abstract class AbstractProcessHandler {/*** 下一关用当前抽象类来接收*/protected AbstractProcessHandler next;public void setNext(AbstractProcessHandler next) {this.next = next;}/*** 具体处理逻辑* 需要子类实现*/public abstract void process();}

第一个流程处理器(此处模拟具体的流程处理对象,具体的处理逻辑写在此处) FirstProcessHandler.java

package com.dxc.responsibility.handler.impl;import com.dxc.responsibility.handler.AbstractProcessHandler;
import lombok.extern.slf4j.Slf4j;/*** 第一个流程处理器** @Author xincheng.du* @Date 2023/8/30 11:25*/
@Slf4j
public class FirstProcessHandler extends AbstractProcessHandler {@Overridepublic void process() {log.info("第一个流程处理开始对请求进行处理......");if (this.next != null) {this.next.process();}}}

第二个流程处理器(此处模拟具体的流程处理对象,具体的处理逻辑写在此处) SecondProcessHandler.java

package com.dxc.responsibility.handler.impl;import com.dxc.responsibility.handler.AbstractProcessHandler;
import lombok.extern.slf4j.Slf4j;/*** 第二个流程处理器** @Author xincheng.du* @Date 2023/8/30 11:25*/
@Slf4j
public class SecondProcessHandler extends AbstractProcessHandler {@Overridepublic void process() {log.info("第二个流程处理开始对请求进行处理......");if (this.next != null) {this.next.process();}}}

第三个流程处理器(此处模拟具体的流程处理对象,具体的处理逻辑写在此处) SecondProcessHandler.java

package com.dxc.responsibility.handler.impl;import com.dxc.responsibility.handler.AbstractProcessHandler;
import lombok.extern.slf4j.Slf4j;/*** 第三个流程处理器** @Author xincheng.du* @Date 2023/8/30 11:25*/
@Slf4j
public class ThirdProcessHandler extends AbstractProcessHandler {@Overridepublic void process() {log.info("第三个流程处理开始对请求进行处理......");if (this.next != null) {this.next.process();}}}

流程扩展类接口(主要对流程扩展类进行封装初始化,为流程处理器工厂提供方法) ProcessService.java

package com.dxc.responsibility.service;import com.dxc.responsibility.dto.ProcessDTO;/*** 流程扩展类 接口** @Author xincheng.du* @Date 2023/8/30 14:26*/
public interface ProcessService {/*** 根据流程处理器id获取流程扩展类** @param handlerId 流程处理器id* @return  {@link ProcessDTO}*/ProcessDTO getProcessEntity(Integer handlerId);/*** 获取第一个流程扩展类** @return {@link ProcessDTO}*/ProcessDTO getFirstProcessDTO();}

流程扩展类接口的具体实现 ProcessServiceImpl.java

package com.dxc.responsibility.service.impl;import com.dxc.responsibility.dto.ProcessDTO;
import com.dxc.responsibility.enums.ProcessChainEnum;
import com.dxc.responsibility.service.ProcessService;
import lombok.extern.slf4j.Slf4j;import java.util.HashMap;
import java.util.Map;/*** 流程扩展类 逻辑处理** @Author xincheng.du* @Date 2023/8/30 14:26*/
@Slf4j
public class ProcessServiceImpl implements ProcessService {/*** 流程扩展类Map* key=流程处理器id value=流程扩展类 ProcessDTO*/private static Map<Integer, ProcessDTO> processDTOMap = new HashMap<>();/*** 流程链初始化* 将枚举中配置的handler初始化到map中,方便获取*/static {ProcessChainEnum[] values = ProcessChainEnum.values();for (ProcessChainEnum value : values) {ProcessDTO processDTO = value.getProcessDTO();processDTOMap.put(processDTO.getHandlerId(), processDTO);}}@Overridepublic ProcessDTO getProcessEntity(Integer handlerId) {return processDTOMap.get(handlerId);}@Overridepublic ProcessDTO getFirstProcessDTO() {for (Map.Entry<Integer, ProcessDTO> entry : processDTOMap.entrySet()) {ProcessDTO value = entry.getValue();//  没有上一个handler的就是第一个if (value.getPreHandlerId() == null) {return value;}}log.error("获取第一个流程扩展类出错");return null;}
}

客户端(相当于请求发起者) ProcessClient.java

package com.dxc.responsibility;import com.dxc.responsibility.factory.ProcessHandlerFactory;
import com.dxc.responsibility.handler.AbstractProcessHandler;/*** 客户端** @Author xincheng.du* @Date 2023/8/30 14:26*/
public class ProcessClient {public static void main(String[] args) {// 获取第一个流程处理器AbstractProcessHandler firstProcessHandler = ProcessHandlerFactory.getFirstProcessHandler();assert firstProcessHandler != null;// 执行第一个流程处理器firstProcessHandler.process();}}

4. 运行结果

在这里插入图片描述

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

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

相关文章

SAFe大规模敏捷框架,敏捷认证培训体系(全)

1. Leading SAFe 课程受众&#xff1a;课程面向决策层、领导者和经理。课程目标&#xff1a;成为一名具备精益敏捷思维的领导者&#xff0c;通过系统化地学习 SAFe&#xff0c;能够领导企业级业务敏捷转型&#xff0c;通过设计思维理解客户需求&#xff0c;实施敏捷产品交付、…

大数据课程L6——网站流量项目的SparkStreaming

文章作者邮箱:yugongshiye@sina.cn 地址:广东惠州 ▲ 本章节目的 ⚪ 了解网站流量项目的SparkStreaming概述; ⚪ 掌握网站流量项目的SparkStreaming实现 Wordcount 底层流程; ⚪ 掌握网站流量项目的SparkStreaming实现历史批次的累积处理; ⚪ 掌握网站流…

快速学会git版本管理——上传gitee仓库

首先在gitee右上角有一个新建仓库 创建之后打开自己想要上传的文件 右键打开 Git Bash Here 接下来会弹出git的窗口 首先先初始化仓库 用git命令 git init 然后用git add . 上传所有文件上传到暂存区(上一篇文章说过add是单个文件&#xff0c;add . 是所有文件) 没有显示错误 …

算法训练营第四十四天(9.6)| 动态规划Part17

目录 Leecode 647.回文子串 Leecode 516.最长回文子序列 Leecode 647.回文子串 题目地址&#xff1a;力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 题目类型&#xff1a;回文 class Solution { public:int countSubstrings(string s) {int n s.si…

OpenCV_CUDA_VS编译安装

一、OpenCV 我这里是下载的OpenCV4.5.4&#xff0c;但是不知道到在vs里面build时一直报错&#xff0c;后面换了4.7.0的版本测试&#xff0c;安装成功。 Release OpenCV 4.5.4 opencv/opencv GitHub 这个里面有官方预编译好的OpenCV库&#xff0c;可以直接食用。 扩展包&am…

Python数据类型的相互转换

简单数据类型之间的转换 1.字符串如果是数字的&#xff0c;转换为int类型 a "10" a int(a) print(a) 2.数字类型转换成bool类型 a 10 a bool(a) print(a) 只有0才是false&#xff0c;其他值是True 复杂数据类型之间的转换 list&#xff1a;列表 tuple&…

SQL4 查询结果限制返回行数

描述 题目&#xff1a;现在运营只需要查看前2个用户明细设备ID数据&#xff0c;请你从用户信息表 user_profile 中取出相应结果。 示例&#xff1a; iddevice_idgenderageuniversityprovince12138male21北京大学Beijing23214male复旦大学Shanghai36543female20北京大学Beijin…

Golang 中的静态类型和动态类型

定义说明 静态类型&#xff08;static type&#xff09;&#xff1a;在编码时就能确定的类型&#xff0c;通过变量定义可以确定的类型&#xff1b;动态类型&#xff08;concrete type&#xff09;&#xff1a;在运行时才能确定具体的数据类型&#xff1b; 动态静态类型如何理…

设计模式(1) - UML类图

1、前言 从这一节开始&#xff0c;我们将一起学习设计模式。我们的学习目标是什么呢&#xff1f; 了解常用设计模式以及它们的使用场景&#xff1b;分析实际工程中设计模式的使用&#xff0c;揣摩实际意图&#xff0c;了解作者设计思路&#xff1b;尝试运用设计模式迭代、重构…

css transition 指南

css transition 指南 在本文中&#xff0c;我们将深入了解 CSS transition&#xff0c;以及如何使用它们来创建丰富、精美的动画。 基本原理 我们创建动画时通常需要一些动画相关的 CSS。 下面是一个按钮在悬停时移动但没有动画的示例&#xff1a; <button class"…

qt之movetothread理解

基础概念 qt的下线程qthread&#xff0c;每个线程都有自己的事件循环exec。对象的线程上下文&#xff0c;每个对象都有自己的线程上下文&#xff0c;怎么理解呢&#xff0c;就是该对象在哪个线程创建&#xff0c;其线程上下文就是谁。每个qobject对象在创建时都有包含线程成员…

MySQL下载安装环境变量配置,常用命令

一、下载安装 mysql官网 下载连接 这个是下载图形安装 https://dev.mysql.com/downloads/installer/ 这个是下载免图形安装 https://dev.mysql.com/downloads/mysql/ 担心个别宝宝没有账号&#xff0c;这边也提供一下&#xff0c;方便下载&#xff1a; 账户&#xff1a;1602404…

Spring Cloud 面试题总结

Spring Cloud和各子项目版本对应关系 Spring Cloud 是一个用于构建分布式系统的开发工具包&#xff0c;它基于Spring Boot提供了一组模块和功能&#xff0c;用于构建微服务架构中的分布式应用程序。Spring Cloud的不同子项目有各自的版本&#xff0c;下面是一些常见的Spring C…

使用 Pandera 的 PySpark 应用程序的数据验证

推荐&#xff1a;使用 NSDT场景编辑器 快速搭建3D应用场景 本文简要介绍了 Pandera 的主要功能&#xff0c;然后继续解释 Pandera 数据验证如何与自最新版本 &#xff08;Pandera 0.16.0&#xff09; 以来使用本机 PySpark SQL 的数据处理工作流集成。 Pandera 旨在与其他流行…

linux 压缩webfile文件夹 webfile.tar.gz和webfile.tar的区别

linux 压缩webfile文件夹 在Linux中&#xff0c;你可以使用tar命令来压缩文件夹。以下是将文件夹压缩为名为"webfile.tar"的示例命令&#xff1a; cd到webfile所在的文件夹&#xff0c;然后执行 tar -cvf webfile.tar webfile/上述命令中&#xff0c;-c选项表示创建…

DNS(域名解析系统)

含义 当我们在上网要访问莫个服务器的时候&#xff0c;就需要知道服务器的IP地址&#xff0c;但IP地址是一串数字&#xff0c;虽然这串数字用点分十进制已经清晰不少了&#xff0c;但还是不利于人们记忆和传播&#xff0c;于是人们使用单词来代替IP地址&#xff08;例如baidu&a…

Jira 笔记

目录 1. Jira 笔记1.1. 项目管理工具 JIRA 实践指导1.2. JIRA 1. Jira 笔记 1.1. 项目管理工具 JIRA 实践指导 https://zhuanlan.zhihu.com/p/619453520?utm_id0 1、JIRA 序言篇1.1 为什么要使用项目管理工具1.2 项目管理工具分析与比较二 JIRA 配置篇2.1 JIRA 配置之问题类…

算法基础-数学知识-容斥原理、博弈论

容斥原理、博弈论 容斥原理890. 能被整除的数&#xff08;二进制状态压缩版本&#xff0c;复杂度多一个Om&#xff09;890. 能被整除的数&#xff08;dfs版本&#xff09; 博弈论无限制nim游戏AcWing 891. Nim游戏AcWing 892. 台阶-Nim游戏&#xff08;待补&#xff09; 集合版…

Linux中防火墙的简单使用方法

目录 前言 ​编辑 一、概念 1、防火墙的分类&#xff1a; 2、防火墙性能 3、硬件防火墙的品牌、软件防火墙的品牌 4、硬件防火墙与软件防火墙比较 二、linux中的防火墙 1、iptables 2.netfilter/iptables功能 3、四表 iptables中表的优先级 4、五链 三、iptables…

数字化转型背景下企业知识管理能力提升路径

近年来&#xff0c;科技不断进步&#xff0c;颠覆性技术&#xff08;例如 5G、云计算、物联网、大数据分析和人工智能等&#xff09;正在重新定义企业如何管理项目和运营效率。知识管理体系亦需要随着科技的进步而改变&#xff0c;以适应新的数字时代环境&#xff0c;并且高效知…