责任链模式与spring容器的搭配应用

背景

有个需求,原先只涉及到一种A情况设备的筛选,每次筛选会经过多个流程,比如先a功能,a功能通过再筛选b功能,然后再筛选c功能,以此类推。现在新增了另外一种B情况的筛选,B情况同样需要A情况的筛选流程,并且需要在A情况的基础上,新增另外的功能筛选,这里假设A需要a、b、c功能的筛选,而B需要a、b、c、d功能的筛选,并且这些功能的筛选的顺序可能发生变动,比如新增了某个筛选,这个筛选涉及到的计算量少那肯定可以把这个置在前面先处理,不满足条件就return,咋一看,这个需求很符合责任链模式的应用场景,下面介绍编码。这里的代码参考了 马丁玩编程 在其12306项目里面的责任链模式,并做出一些相应改动,以适配当前的场景。

代码

责任链模式顶层接口

这里继承了Ordered类,是为了方便后续对处理器进行排序。

public interface AbstractChainHandler<REQUEST> extends Ordered {

    default boolean handler(REQUEST requestParam){

        return true;
    };
    
}

A情况的接口和B情况的接口。

public interface DeviceTypeAChainFilter extends AbstractChainHandler<DeviceFilterBO> {

}

public interface DeviceTypeBChainFilter extends AbstractChainHandler<DeviceFilterBO> {

}

定义成接口,后续往里面添加处理器的时候,方便查看当前A规则和B规则都有哪些处理器:

image-20240206171639069

具体的处理器

处理器1:

@Component
public class DeviceFunctionChainHandler implements DeviceTypeAChainFilter, DeviceTypeBChainFilter {

    @Override
    public boolean handler(DeviceFilterBO deviceFilterBO) {
        if (deviceFilterBO.getDeviceBO().getCondition() % 2 == 0) {
            System.out.println("处理器A:筛选功能不通过");
            return false;
        }
        // 筛选功能
        System.out.println("处理器A:筛选功能通过");
        return true;
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

处理器2:

@Component
public class DeviceResolutionChainHandler implements DeviceTypeAChainFilter, DeviceTypeBChainFilter {

    @Override
    public boolean handler(DeviceFilterBO deviceFilterBO) {
        // 分辨率支持
        System.out.println("处理器B:分辨率支持");
        return true;
    }

    @Override
    public int getOrder() {
        return 10;
    }

}

处理器3:

@Component
public class DeviceCaculateOutputChainHandler implements DeviceTypeBChainFilter {

    @Override
    public boolean handler(DeviceFilterBO deviceFilterBO) {
        // 接口支持
        System.out.println("处理器C:输出接口支持");
        // 计算设备数量满足要求
        System.out.println("处理器C:根据输出接口计算的设备数量满足要求");
        return true;
    }

    @Override
    public int getOrder() {
        return 30;
    }
}

处理器4:

@Component
public class DeviceCaculateInputChainHandler implements DeviceTypeAChainFilter, DeviceTypeBChainFilter {

    @Override
    public boolean handler(DeviceFilterBO deviceFilterBO) {
        if (deviceFilterBO.getDeviceBO().getCondition() % deviceFilterBO.getCondition() == 0) {
            System.out.println("处理器D:输入接口不支持");
            return false;
        }
        ArrayList<DeviceBO> deviceRes = (ArrayList<DeviceBO>) AbstractChainContext.threadLocal.get();
        deviceRes.add(deviceFilterBO.getDeviceBO());
        // 接口支持
        System.out.println("处理器D:输入接口支持");
        // 计算设备数量满足要求
        System.out.println("处理器D:根据输入接口计算的设备数量满足要求");
        return true;
    }

    @Override
    public int getOrder() {
        return 40;
    }
}

可以看到,处理器都用@Component进行标识,后续通过ioc容器获取这些处理器进行分类和执行。并且,可以看到A..filter接口有三个实现者,这说明A有三种处理器,同理B有四种处理器,并且由于顶层接口继承了Order类,所有具体的处理器都会标识当前的order,如上面的10,20,30...这里把Order的数字间隔放大一些,比如10,20,30,如果以后要往这些间隔插入新的处理逻辑也方便。

获取具体处理器和执行hanlder的上下文类

先将不同的处理规则的接口都放在某个特定包下

image-20240206172405349

先去扫描这个包下的所有接口,然后再去Spring Ioc容器里面拿出这些接口的实现类,把不同的接口实现类按接口名字作为标识,按Order对这些实现类进行排序,然后放到一个List里面,以接口名字作为key,实现类List作为value,后续调用链式调用的时候,传入具体的接口名字(处理规则名字),实现链式顺序调用,具体实现如下

AbstractChainContext上下文类:

public final class AbstractChainContext<REQUEST, RESPONSE> implements CommandLineRunner {

    private final static Map<String, List<AbstractChainHandler>> abstractChainHandlerContainer = new HashMap<>();

    public final static ThreadLocal threadLocal = new ThreadLocal<>();

    public void handler(String mark, REQUEST requestParam) {
        List<AbstractChainHandler> abstractChainHandlers = abstractChainHandlerContainer.get(mark);
        if (CollectionUtils.isEmpty(abstractChainHandlers)) {
            throw new RuntimeException(String.format("[%s] Chain of Responsibility ID is undefined.", mark));
        }
        for (AbstractChainHandler abstractChainHandler : abstractChainHandlers) {
            if(!abstractChainHandler.handler(requestParam)){
                break;
            }
        }
    }


    @Override
    public void run(String... args) {
        List<Class<?>> interfaces = getInterfacesInPackage("com.zh.demo.designpattern.chain.type");
        for (Class<?> interfaceType : interfaces) {
            Map<String, AbstractChainHandler> beansOfType = (Map<String, AbstractChainHandler>) ApplicationContextHolder.getBeansOfType(interfaceType);
            // 转成list
            List<AbstractChainHandler> sortedList = beansOfType.values().stream()
                    .sorted(Comparator.comparing(Ordered::getOrder))
                    .collect(Collectors.toList());
            int index = interfaceType.getName().lastIndexOf(".") + 1;
            abstractChainHandlerContainer.put(interfaceType.getName().substring(index), sortedList);
        }
    }

    public static List<Class<?>> getInterfacesInPackage(String packageName) {
        List<Class<?>> result = new ArrayList<>();
        try {
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            String path = packageName.replace('.', '/');
            Enumeration<URL> resources = classLoader.getResources(path);

            while (resources.hasMoreElements()) {
                URL resource = resources.nextElement();
                File directory = new File(resource.getFile());
                File[] files = directory.listFiles();

                if (files != null) {
                    for (File file : files) {
                        if (file.getName().endsWith(".class")) {
                            String className = packageName + '.' + file.getName().replace(".class", "");
                            Class<?> clazz = Class.forName(className);

                            if (clazz.isInterface()) {
                                result.add(clazz);
                            }
                        }
                    }
                }
            }
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }

        return result;
    }
}

在上面变量中,用了个 public final static ThreadLocal threadLocal = new ThreadLocal<>(); 这个是用来保存设备的筛选列表。

定义好不同筛选规则的枚举类:

public enum DeviceChainMarkEnum {

    /**
     * A设备过滤器
     */
    DEVICE_TYPEA_FILTER("DeviceTypeAChainFilter"),

    /**
     * B设备过滤器
     */
    DEVICE_TYPEB_FILTER("DeviceTypeBChainFilter");

    String name;

    public String getName() {
        return name;
    }

    DeviceChainMarkEnum(String name) {
        this.name = name;
    }

}

Service的编写

@Service
@RequiredArgsConstructor
@Slf4j
public class DemoServiceImpl implements DemoService {

    private final AbstractChainContext<DeviceFilterBO, Object> devcieTypeChainContext;

    @Override
    public List<DeviceBO> filterDeviceTypeA(ParmDTO parmDTO) {
        ArrayList<DeviceBO> deviceList = new ArrayList<>();
        // 简化条件
        parmDTO.setCondition(2);
        // 实际情况应该是从数据库读取设备的信息
        for (int i = 0; i < 5; i++) {
            DeviceBO deviceDTO = DeviceBO.builder().condition(new Random().nextInt(100)).build();
            deviceList.add(deviceDTO);
        }
        ArrayList<DeviceBO> deviceRes = new ArrayList<>();
       // 把需要的结果放到threadLocal中,在具体的处理器中对结果List进行处理
        AbstractChainContext.threadLocal.set(deviceRes);
        // 筛选多个设备 对符合的设备加入到deviceRes
        for (DeviceBO deviceBo : deviceList) {
            DeviceFilterBO deviceFilterBO = DeviceFilterBO.builder().condition(parmDTO.getCondition()).deviceBO(deviceBo).build();
            // 以A规则进行处理
            devcieTypeChainContext.handler(DeviceChainMarkEnum.DEVICE_TYPEA_FILTER.getName(), deviceFilterBO);
        }
        AbstractChainContext.threadLocal.remove();
        System.out.println("筛选结果数量:" + deviceRes.size());
        return deviceRes;
    }

    @Override
    public List<DeviceBO> filterDeviceTypeB(ParmDTO parmDTO) {
        ArrayList<DeviceBO> deviceList = new ArrayList<>();
        // 简化条件
        parmDTO.setCondition(2);
        // 实际情况应该是从数据库读取设备的信息
        for (int i = 0; i < 5; i++) {
            DeviceBO deviceDTO = DeviceBO.builder().condition(new Random().nextInt(100)).build();
            deviceList.add(deviceDTO);
        }
        ArrayList<DeviceBO> deviceRes = new ArrayList<>();
        // 把需要的结果放到threadLocal中,在具体的处理器中对结果List进行处理
        AbstractChainContext.threadLocal.set(deviceRes);
        // 筛选多个设备 对符合的设备加入到deviceRes
        for (DeviceBO deviceBo : deviceList) {
            DeviceFilterBO deviceFilterBO = DeviceFilterBO.builder().condition(parmDTO.getCondition()).deviceBO(deviceBo).build();
            // 以B规则进行处理
            devcieTypeChainContext.handler(DeviceChainMarkEnum.DEVICE_TYPEB_FILTER.getName(), deviceFilterBO);
        }
        AbstractChainContext.threadLocal.remove();
        System.out.println("筛选结果数量:" + deviceRes.size());
        return deviceRes;
    }

}

这里假设有五种设备,每个设备通过DeviceBO里面的condition设置条件,演示一遍筛选过程

DeviceBO类:

@Builder
@Data
public class DeviceBO {

    private int condition;

}

演示筛选规则A,一共五个设备数据,只有一个筛选通过了,这里涉及到A,B,D三种处理器

image-20240222103239661

演示筛选规则B,一共五个设备数据,2个筛选通过了,这里涉及到A,B,C,D三种处理器

image-20240222103338406

文章转载自:John-zh

原文链接:https://www.cnblogs.com/Johnyzh/p/18026938

体验地址:引迈 - JNPF快速开发平台_低代码开发平台_零代码开发平台_流程设计器_表单引擎_工作流引擎_软件架构

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

软件设计师软考题目解析02 --每日五题

想说的话&#xff1a;要准备软考了。0.0&#xff0c;其实我是不想考的&#xff0c;但是吧&#xff0c;由于本人已经学完所有知识了&#xff0c;只是被学校的课程给锁在那里了&#xff0c;不然早找工作去了。寻思着反正也无聊&#xff0c;就考个证玩玩。 本人github地址&#xf…

网络中的进程监控

每个企业都有一些流程和程序来实现他们的业务目标&#xff0c;这同样适用于网络&#xff0c;网络中的进程监控是分析、处理和管理网络内发生的各种活动以提高网络性能和能力的做法。 网络中需要监控的基本进程 监视系统资源&#xff08;CPU 利用率、内存利用率、CPU 温度等&a…

ChatGPT在数据分析学习阶段的应用

ChatGPT在数据分析学习阶段的应用 ​ 这个阶段&#xff0c;核心是三件事&#xff1a;制定学习计划、确定学习资料以及学习策略。我们可以自己完成这几件事&#xff0c;当然也可以借助ChatGPT来高效地达到目的。 1.1 制定学习计划 ​ 学习阶段的第一件事是制定学习计划&#…

红队评估四靶场

文章目录 环境搭建1.设置所需网卡2.更改win7设置3.DC设置4.web设置开启docker服务5.kali网段`渗透启动`1.确认对方靶机的IP地址2.端口探测3.web探测`2001端口``2002端口`Tomcat/8.5.19漏洞复现`2003端口`4.docker逃逸5.ssh密钥爆破`域渗透启动`1.提权2.隧道搭建各项配置文件内容…

自助点餐系统微信小程序,支持外卖、到店等

总体介绍 系统总共分为三个端&#xff1a;后端&#xff0c;后台管理系统、微信小程序。 基于当前流行技术组合的前后端分离商城系统&#xff1a; SpringBoot2MybatisPlusSpringSecurityjwtredisVue的前后端分离的商城系统&#xff0c; 包含分类、sku、积分、多门店等 预览图…

JavaSE-04笔记【面向对象01】

文章目录 1. final 关键字1.1 采用final修饰的类不能被继承1.2 采用 final 修饰的方法不能被覆盖1.3 采用 final 修饰的变量(基本类型)不能被修改1.4 采用final 修饰的变量必须显示初始化1.5 如果修饰的引用&#xff0c;那么这个引用只能指向一个对象&#xff0c;也就是说这个引…

OpenBMC的c++代码中的变量初始化问题(二)

1 开发平台 Win11、VS2022、Fedora39。 2 作业目的 通过VS2022跨平台Linux构建openbmc/intel-ipmi-oem的x64可执行模块。 3 问题描述 该模块启动后&#xff0c;出现以下异常&#xff1a; 上图中的调用堆栈消息是&#xff1a; std::__uniq_ptr_impl<sd_bus, sdbusplus::…

Windows下VTK 源码编译(For Qt PCL)

虽然我们在windows下安装PCL的时候就已经安装了VTK&#xff0c;由于跟着PCL安装的VTK是没有和QT联合编译的&#xff0c;所以在使用PCL和QT做点云可视化界面的时候是无法使用QT的插件QVTKWidget。 VTK 源码下载 Tags VTK / VTK GitLab 我这里的环境是Win10 Visual Studio&…

Android 相机启动流程笔记

和你一起终身学习&#xff0c;这里是程序员Android 经典好文推荐&#xff0c;通过阅读本文&#xff0c;您将收获以下知识点: 一、Camera 框架介绍&#xff1a; Camera的框架分为Kernel部分和hal部分&#xff0c;其中kernel部分主要有两块&#xff1a; image sensor driver&…

【计算机学院寒假社会实践】——卫生服务无限情,社区居民乐融融

为了加强社区基层党组织建设和改进社区工作&#xff0c;推动社区更好繁荣发展&#xff0c;曲阜师范大学计算机学院“青年扎根基层&#xff0c;服务走进社区”实践队员周兴睿在2024年2月14日来到了山东省滨州市陈集社区&#xff0c;对社区卫生进行清洁工作。 这一年&#xff0c;…

[剪藏] - “闷声赚票房”的雷佳音,“窝囊废赛道”的成功学代表?

作者| 糖炒山楂 编辑| Mia 猫眼专业版上&#xff0c;雷佳音主演电影票房达到了147亿&#xff0c;排在影人榜第12名。按照平台对《热辣滚烫》和《第二十条》最终票房的预测&#xff0c;他的个人票房还有10亿左右的上升空间。 相比贾玲、赵丽颖等女性扛起了春节档的焦点话题&a…

微服务Day6

文章目录 DSL查询文档RestClient查询文档快速入门 旅游案例 DSL查询文档 RestClient查询文档 快速入门 Testvoid testMatchAll() throws IOException {//1.准备RequestSearchRequest request new SearchRequest("hotel");//2.准备DSLrequest.source().query(QueryB…

计算机网络实验四VLAN与三层交换机

一、实验目的和要求 1&#xff09;掌握VLAN的基本配置方法&#xff0c;理解VLAN的功能和作用&#xff1b; 2&#xff09;掌握三层交换机的基本配置方法。 二、实验环境 1&#xff09;运行Windows 2008 Server/XP/7操作系统的PC一台。 2&#xff09;PacketTracer。 实验内…

H12-821_29

29.四台路由器运行IS-S且已经建立邻接关系,区域号和路由器的等级如图中标记,下列说法中正确的有? A.R2和R3都会产生ATT置位的Level-1的LSP B.R1没有R4产生的LSP,因此R1只能通过缺省路由和R4通信 C.R2和R3都会产生ATT置位的Leve1-2的LSP D.R2和R3互相学习缺省路由,该网络出现路…

Github 2024-02-23 开源项目日报 Top10

根据Github Trendings的统计&#xff0c;今日(2024-02-23统计)共有10个项目上榜。根据开发语言中项目的数量&#xff0c;汇总情况如下&#xff1a; 开发语言项目数量非开发语言项目4Python项目3TypeScript项目1HTML项目1Dart项目1Rust项目1 从零开始构建你喜爱的技术 创建周…

CVE-2023-44313 Apache ServiceComb Service-Center SSRF 漏洞研究

本次项目基于go语言&#xff08;本人不精通&#xff09;&#xff0c;虽不是java web框架了 &#xff0c;但搭建web服务的框架一些思想理念却是通用的&#xff0c;我们由此可以得到一些蛛丝马迹....... 目录 漏洞简介 漏洞分析 漏洞复现 漏洞简介 Apache ServiceComb Servi…

PCIe和SATA接口统计

一、PCIe接口 1、Add-in-Card(AIC) AIC是最常见的PCIe接口形态,组装过电脑的同学可能比较清楚,电脑上的主板上都会有下面的几排插槽,这就是典型的PCIe AIC的插槽,比较常见的插槽位宽为x16和x1 插在上面的卡就是PCIe AIC。PCIe AIC常见的有显卡,无线网卡,存储设备等等 A…

Javaweb之SpringBootWeb案例之AOP案例的详细解析

4. AOP案例 SpringAOP的相关知识我们就已经全部学习完毕了。最后我们要通过一个案例来对AOP进行一个综合的应用。 4.1 需求 需求&#xff1a;将案例中增、删、改相关接口的操作日志记录到数据库表中 就是当访问部门管理和员工管理当中的增、删、改相关功能接口时&#xff0c…

Python入门学习——基础语法

一、Python解释器 1. Python解释器的作用是&#xff1a; 将Python代码翻译成计算机认识的O和1并提交计算机执行在解释器环境内可以一行行的执行我们输入的代码也可以使用解释器程序&#xff0c;去执行".py"代码文件 2. Python解释器程序在&#xff1a; <Python…

c++的类型转换方法

一、静态类型转换&#xff08;static_cast&#xff09; 静态类型的转换主要用于基本类型之间的转换&#xff0c;比如int类型转换为double类型。但是static_cast也可以支持上下行的转换&#xff08;存在继承关系之间的转换&#xff09; 基本类型之间的转换举例 上下行转换的举…