当前位置: 首页 > news >正文

设计模式 - 责任链

一、前言

​ 相信大家平时或多或少都间接接触过责任链设计模式,只是可能有些同学自己不知道此处用的是该设计模式,比如说 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.lryc.cn/news/161019.html

相关文章:

  • 【小沐学Unity3d】3ds Max 骨骼动画制作(CAT、Character Studio、Biped、骨骼对象)
  • CUDA说明和安装[window]
  • sqlserver2012性能优化配置:设置性能相关的服务器参数
  • 介绍 dubbo-go 并在Mac上安装,完成一次自己定义的接口RPC调用
  • 目标检测数据集:摄像头成像吸烟检测数据集(自己标注)
  • Unity的UI管理器
  • Mp4文件提取详细H.264和MP3文件
  • Qt应用程序连接达梦数据库-飞腾PC麒麟V10
  • 2023-09-03 LeetCode每日一题(消灭怪物的最大数量)
  • 绘图 | MATLAB
  • 2023年下半年高项考试学习计划
  • SpringBoot中CommandLineRunner的使用
  • <OpenCV> Mat属性
  • LAMP 综合实验
  • JavaScript发展历程
  • LP(六十九)智能文档助手升级
  • VIM统计搜索关键词命令
  • 0017Java程序设计-spr农业过程化管理系统
  • 以可视化方式解释 Go 并发 - 通道
  • kafka学习-生产者
  • 【Python】设计模式
  • C++ 数字
  • code阶段——gitgitlab安装
  • C 风格文件输入/输出---无格式输入/输出
  • Spring-MVC的文件上传下载,及插件的使用(让项目开发更节省时间)
  • 算法 数据结构 递归冒泡算法 java冒泡算法 优化递归冒泡 数据结构(九)
  • 【计算机视觉 | 目标检测】目标检测常用数据集及其介绍(十五)
  • 洛谷P8814:解密 ← CSP-J 2022 复赛第2题
  • Flutter实现CombineExecutor进行多个异步分组监听,监听第一个异步执行的开始和最后一个异步执行结束时机。
  • 2023 年最新Java 毕业设计选题题目参考,500道 Java 毕业设计题目,值得收藏