• 设计模式 - 责任链


    一、前言

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

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

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

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

    责任链模式有哪些优点,能解决什么问题,我们为什么要使用它呢?
    优点:
    • 将请求与处理解耦。
    • 请求处理对象只需关注自己需要处理的请求进行处理即可,对于不需要自己处理的请求,直接转发给下一个处理对象即可,符合单一职责原则。
    • 具备链式传递处理请求功能,请求发送者无需知晓链路结构,只需等待请求处理结果。
    • 链路结构灵活,可以通过改变链路结构动态的新增或者删除处理对象。即易于拓展新的请求处理类,符合开闭原则。
    缺点:
    • 会存在责任链太长,而大多数处理者都不会对请求进行处理的情况,导致走完整个责任链的时间太长,影响整体性能。
    • 如果责任链配置不完善,会存在处理对象循环引用,从而造成死循环,导致系统崩溃的情况。
    适用场景:
    • 多条件流程判断,如权限控制
    • ERP 系统流程审批
    • Java Web 过滤器的底层实现 Filter
    • Mybatis 中的分页插件 PageHelper

    二、代码示例

    1. 导包信息

    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-webartifactId>
    dependency>
    
    <dependency>
        <groupId>cn.hutoolgroupId>
        <artifactId>hutool-allartifactId>
        <version>5.8.16version>
    dependency>
    
    <dependency>
        <groupId>org.projectlombokgroupId>
        <artifactId>lombokartifactId>
        <version>1.18.12version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    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;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    流程链枚举 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;
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    流程处理器工厂(主要用来初始化流程链,并返回第一个流程处理器) 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;
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74

    抽象流程处理器(主要是用来定义每个具体处理的方法模板,不做具体逻辑处理,具体的流程处理器需要集成当前抽象类,并实现各自的流程处理逻辑) 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();
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    第一个流程处理器(此处模拟具体的流程处理对象,具体的处理逻辑写在此处) 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();
            }
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    第二个流程处理器(此处模拟具体的流程处理对象,具体的处理逻辑写在此处) 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();
            }
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    第三个流程处理器(此处模拟具体的流程处理对象,具体的处理逻辑写在此处) 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();
            }
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    流程扩展类接口(主要对流程扩展类进行封装初始化,为流程处理器工厂提供方法) 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();
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    流程扩展类接口的具体实现 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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    客户端(相当于请求发起者) 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();
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    4. 运行结果

    在这里插入图片描述

  • 相关阅读:
    用户粘性︱如何提升用户忠诚度
    从理解js双重递归执行顺序到用递归方式实现二叉树中序遍历
    聚苯乙烯核-聚(丙烯酰胺-丙烯酸)壳荧光素微球/磺酸官能化聚苯乙烯高荧光微球的制备
    SMT贴片制造:专业、现代、智能的未来之选
    屎山代码踩坑记录:不要将一个类写的臃肿
    多线程服务器适用场合
    (脑肿瘤分割笔记:六十二)缺失模态下的对抗性联合训练脑肿瘤分割网络
    RabbitMQ基础概念-02
    Android 13 新特性及适配指南
    训练营第三十五天动态规划(基础题part1)
  • 原文地址:https://blog.csdn.net/weixin_44248000/article/details/132303938