设计模式 - 责任链

news/2024/5/19 12:55:40 标签: 设计模式, java, 责任链模式

一、前言

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

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

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

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

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

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

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

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

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

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

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.niftyadmin.cn/n/5004099.html

相关文章

Android图形-架构2

目录 引言 组件概述 底层组件&#xff1a; 上层组件&#xff1a; 引言 Android图形架构主要涉及些什么呢&#xff1f; 1&#xff09;Android 系统级图形架构的基本元素&#xff1f; 2&#xff09;应用框架和多媒体系统如何使用这些元素&#xff1f; 3&#xff09;图形数据…

GPT引领前沿热点、AI绘图

GPT对于每个科研人员已经成为不可或缺的辅助工具&#xff0c;不同的研究领域和项目具有不同的需求。如在科研编程、绘图领域&#xff1a; 1、编程建议和示例代码: 无论你使用的编程语言是Python、R、MATLAB还是其他语言&#xff0c;都可以为你提供相关的代码示例。 2、数据可…

手机便签功能在哪里?如何在便签里添加文字图片视频?

手机已成为我们生活中不可或缺的工具&#xff0c;而在使用手机的过程中&#xff0c;我们经常需要随手记录一些重要的事情。那么&#xff0c;如何高效便捷地记录这些事情呢&#xff1f;答案就是使用手机便签软件。但是&#xff0c;有很多人不知道手机便签功能在哪里&#xff1f;…

Ubuntu之apt-get系列--apt-get安装软件的方法/教程

原文网址&#xff1a;Ubuntu之apt-get系列--apt-get安装软件的方法/教程_IT利刃出鞘的博客-CSDN博客 简介 本文介绍Ubuntu使用apt-get安装软件的方法。 安装软件 先更新列表 sudo apt-get update 安装软件 sudo apt-get install <package name>[<version>]…

ConfigMaps in K8s

摘要 ConfigMaps是Kubernetes&#xff08;K8s&#xff09;中用于存储应用程序配置信息的一种资源对象。它将key-value对存储为Kubernetes集群中的一个资源&#xff0c;并可以在Pod中以卷或环境变量的形式使用。 ConfigMaps的设计目的是将应用程序配置与应用程序本身解耦。它可…

Linux 访问进程地址空间函数 access_process_vm

文章目录 一、源码解析二、Linux内核 用途2.1 ptrace请求2.2 进程的命令行 参考资料 一、源码解析 /*** get_task_mm - acquire a reference to the tasks mm** Returns %NULL if the task has no mm. Checks PF_KTHREAD (meaning* this kernel workthread has transiently a…

JavaScript中的原型链(prototype chain)

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ JavaScript中的原型链⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏…

在C++和C中static关键字的用法,在C++和C中const关键字的用法

1、在C和C中static关键字的用法 答&#xff1a;static关键字表示存储类型&#xff0c;修饰数据类型。在C语言中&#xff0c;static修饰局部变量&#xff0c;内存分配在静态区&#xff0c;生命周期延长&#xff0c;作用域不变。static修饰全局变量&#xff0c;内存分配在静态区…