设计模式 - 责任链

news2024/11/23 3:53:41

一、前言

​ 相信大家平时或多或少都间接接触过责任链设计模式,只是可能有些同学自己不知道此处用的是该设计模式,比如说 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;
        }
        // 返回第一个handler
        return (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 {

    @Override
    public 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 {

    @Override
    public 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 {

    @Override
    public 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);
        }
    }

    @Override
    public ProcessDTO getProcessEntity(Integer handlerId) {
        return processDTOMap.get(handlerId);
    }

    @Override
    public 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.coloradmin.cn/o/975209.html

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

相关文章

智慧电力系统

智慧电力系统依托电易云-智慧电力物联网&#xff0c;将人工智能技术、物联网技术与电力行业相结合&#xff0c;通过数据的收集、分析和应用&#xff0c;实现电力资源精准调度、安全稳定运行、智能管理的全过程解决方案。 智慧电力系统的核心理念是将科技与能源紧密结合&#x…

SECOND:Sparsely Embedded Convolutional Detection

论文背景 为了克服图像单独提供空间信息的不足&#xff0c;点云数据在三维应用中变得越来越重要。点云数据包含精确的深度信息&#xff0c;可以由LiDAR或RGB-D相机生成。 VoxelNet&#xff1a;首先将点云数据分组成体素&#xff0c;然后在将体素转换成密集的3D张量用于区域提…

C语言插入排序

前言&#xff1a; 本文主要讲解插入排序中的直接插入排序和希尔排序。 1、直接插入排序&#xff1a; 1.1基本思想 直接插入排序是一种简单的插入排序法&#xff0c;其基本思想是把待排序的数值按照大小顺序逐个插入到一个已经排好序的有序序列中&#xff0c;直到将所有记录…

骨传导和普通耳机哪个危害大?哪款更值得入手一些?

先说结论&#xff0c;普通耳机对人体的危害要比骨传导耳机大一些&#xff0c;区分方式也很简单&#xff0c;想要了解骨传导耳机和普通耳机哪个对人体的危害更大&#xff0c;首先就要了解它们的传声方式&#xff0c;普通耳机的传声原理很简单&#xff0c;利用空气传声&#xff0…

App自动化测试持续集成效率提高50%

持续集成是一种开发实践&#xff0c;它倡导团队成员需要频繁的集成他们的工作&#xff0c;每次集成都通过自动化构建&#xff08;包括编译、构建、自动化测试&#xff09;来验证&#xff0c;从而尽快地发现集成中的错误。让正在开发的软件始终处于可工作状态&#xff0c;让产品…

A6500-CC 9199-00120 基于时间或基于条件的工作流

A6500-CC 9199-00120 基于时间或基于条件的工作流 B-Scada宣布VoT(虚拟化事物),允许真实世界的对象和其他数据源在虚拟模型中表示&#xff0c;该模型可以从任意数量的不同来源检索实时数据。虚拟化支持在结构化环境中对这些对象进行实时监控和管理&#xff0c;该环境可以在任…

[杂谈]-快速了解LoRa和LoRaWAN

快速了解LoRa和LoRaWAN 文章目录 快速了解LoRa和LoRaWAN1、什么是LoRa2、什么是 LoRaWAN3、LoRa和LoRaWAN比较4、LoRa与其他无线技术比较5、为什么需要LoRaWAN6、LoRa的缺点7、LoRa联盟8、LoRaWAN标准9、LoRaWAN的应用 许多事物进一步推动工业物联网 (IIoT)&#xff1b; 其中之…

Java 若依框架导出excel添加单表头标题

图示 若依框架通过Excel注解已经封装好导出的工具类了&#xff0c;目前是可以实现简单的单表表头标题添加 在exportExcel方法的后面直接添加一项参数就可以导出添加标题了~

ModaHub魔搭社区:星环科技向量数据库Hippo社区版来啦

大语言模型正在与企业应用迅速结合,并深刻改变企业的各个产业环节。而大模型训练所使用的数据包含了如文档、图片、音视频等各种类型的非结构化数据,传统关系型数据库能力有限。通过将这些非结构化数据转换为多维向量,可以结构化地在向量数据库中进行管理,实现高效的数据存…

DG232RL兼容FT232RL开发资料

芯片上处理的整个 usb 协议——不需要 usb 专用固件编程。 • uart 接口支持7或8个数据位、1或2个停止位和奇数/偶数/标记/空格/无奇偶校验。* 充分辅助的硬件或 x-on/x-off 软件握手。* 数据传输速率从300波特到3兆波特(RS422/RS485和 ttl 级) &#xff0c;从300波特到1兆波特…

终于来啦!OpenDataLab 新增自主上传功能,升级CLI/SDK工具、数据集详情页……体验赠好礼~

9月&#xff0c;OpenDataLab 全新版本上线&#xff0c;支持用户自主发布原创数据集&#xff0c;同时升级了 CLI/SDK 工具、数据集详情页&#xff0c;让 AI 数据集开源更方便、展示更清晰。还有创作领好礼活动&#xff0c;快来看看吧&#xff01; &#xff08;注意&#xff01;…

配电柜监控的真正标准,只有一个!

在现代社会中&#xff0c;电力是我们生活和工作不可或缺的一部分。大楼宾馆作为城市中繁忙生活的一部分&#xff0c;依赖于可靠的电力供应来支持各种设施和服务&#xff0c;从客房照明到电梯运行&#xff0c;无一不离电。然而&#xff0c;大楼宾馆电力分配系统的稳定性和安全性…

Java8新特性 - Lambda表达式

目录 一、Lambda表达式 1.1、为什么使用Lambda表达式&#xff1f; 1.2、Lambda的标准格式 Lambda的标准格式 无参无返回值的Lambda 有参有返回值的Lambda 1.3、Lambda的实现原理 1.4、Lambda省略模式 1.5、Lambda表达式的前提条件 1.6、Lambda与匿名内部类对比 1.7、…

AI智剪,批量剪辑视频的神器

在数字时代&#xff0c;视频剪辑已经成为各种行业中的重要工作。然而&#xff0c;传统的视频剪辑方式既耗时又费力&#xff0c;常常需要大量的时间和人力。幸运的是&#xff0c;随着人工智能技术的发展&#xff0c;我们有了新的解决方案——AI智剪软件。 AI智剪软件&#xff0c…

【gtpJavaScript】使用JavaScript实现套壳gtp与gtp打字输出效果

postman测试gtp接口 https://platform.openai.com/docs/api-reference/chat/create?langcurl 导入到postman中 记得弄一个gtp的key 然后请求测试gtp接口&#xff1a; 纯前端实现gtp请求页面 目录结构&#xff1a; 部分参考&#xff1a;GitHub - xxxjkk/chat-website: 简易版c…

德庄借助纷享销客CRM系统实现高效管理

德庄集团创于1999年&#xff0c;是一家集餐饮产业、食品产业、科技研发及文化研究为一体的现代化民营企业&#xff0c;下属9家子公司、2大现代化食品加工基地、1所研究所、1所培训学校、1个技术中心。拥有德庄、青一色、滟设、香漫谷、饭空等8大子品牌&#xff0c;呈现出良好的…

基于Python的大区域SPI标准降水指数自动批量化处理

1.引言 标准化降水指数&#xff08;SPI&#xff09;是一个广泛使用的指数&#xff0c;用于描述一系列时间尺度上的气象干旱的特征。但是经过研究发现&#xff0c;目前的处理方法基本都是单点进行计算&#xff0c;缺少多点&#xff08;大区域&#xff09;的批量计算过程。因此本…

嵌入式Linux开发实操(十六):Linux驱动模型driver model

嵌入式linux下驱动模型: 1、驱动的绑定 驱动程序绑定driver binding 驱动程序绑定是将设备device与可以控制它的设备驱动程序driver相关联的过程。总线驱动程序bus driver通常会处理,因为有特定于总线bus的结构来表示设备device和驱动程序driver。使用通用的设备device和设…

es倒排索引深入解读

文章目录 一. Lucene二.倒排索引算法2.1 Posting List压缩算法2.1.1 FOR2.1.2 RoaringBitmap压缩 2.3 FST压缩算法2.3.1 trie前缀树原理2.3.2 FST构建过程NFADFAFSMFSAFST:有限状态转换机构建原理FST在lucene中实现原理 1.什么是搜索引擎? 全文搜索引擎: 自然语言处理(NLP)、爬…