背景

有个需求,原先只涉及到一种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规则都有哪些处理器:

具体的处理器

处理器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的上下文类

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

先去扫描这个包下的所有接口,然后再去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三种处理器

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

源码

Johnynzh/chain-of-responsibility-demo: 责任链模式与spring容器的搭配应用 (github.com)

责任链模式与spring容器的搭配应用的更多相关文章

  1. 【责任链模式】责任链模式结合Spring实战Demo

    备注: 责任链与策略模式有很多相似之处,如都是行为型设计模式,都能够处理代码中的if-else逻辑 主要区别在于: 策略模式 封装了算法,通过上下文对象去接受客户端的数据,根据数据类型执行不同的算法 ...

  2. Spring 设计模式之责任链模式

    [应用] 以下是一段代码,Spring MVC 的 diapatcherServlet 的 doDispatch 方法中,获取与请求匹配的处理器(HandlerExecutionChain) getH ...

  3. Spring是如何使用责任链模式的?

    关于责任链模式,其有两种形式,一种是通过外部调用的方式对链的各个节点调用进行控制,从而进行链的各个节点之间的切换. 另一种是链的每个节点自由控制是否继续往下传递链的进度,这种比较典型的使用方式就是Ne ...

  4. tomcat源码解读(2)–容器责任链模式的实现

    责任链模式:责任链模式可以用在这样的场景,当一个request过来的时候,需要对这个request做一系列的加工,使用责任链模式可以使每个加工组件化,减少耦合.也可以使用在当一个request过来的时 ...

  5. java责任链模式及项目实际运用

    1.前言 上次我们认识了java责任链模式的设计,那么接下来将给大家展示责任链模式项目中的实际运用.如何快速搭建责任链模式的项目中运用. 2.简单技术准备 我们要在项目中使用借助这样的几个知识的组合运 ...

  6. java设计模式解析(11) Chain责任链模式

    设计模式系列文章 java设计模式解析(1) Observer观察者模式 java设计模式解析(2) Proxy代理模式 java设计模式解析(3) Factory工厂模式 java设计模式解析(4) ...

  7. 设计模式学习笔记(十四)责任链模式实现以及在Filter中的应用

    责任链模式(Chain Of Responsibility Design Pattern),也叫做职责链,是将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求.当有请求发生时,可将请求沿着这条 ...

  8. [工作中的设计模式]责任链模式chain

    一.模式解析 责任链模式是一种对象的行为模式.在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链.请求在这个链上传递,直到链上的某一个对象决定处理此请求.发出这个请求的客户端并不知 ...

  9. JAVA设计模式之责任链模式

    在阎宏博士的<JAVA与模式>一书中开头是这样描述责任链(Chain of Responsibility)模式的: 责任链模式是一种对象的行为模式.在责任链模式里,很多对象由每一个对象对其 ...

  10. 读书笔记之 - javascript 设计模式 - 责任链模式

    责任链模式可以用来消除请求的发送者和接收者之间的耦合.这是通过实现一个由隐式地对请求进行处理的对象组成的链而做到的.链中的每个对象可以处理请求,也可以将其传给下一个对象. 责任链的结构: 责任链由多个 ...

随机推荐

  1. [转帖]高性能 -Nginx 多进程高并发、低时延、高可靠机制在百万级缓存 (redis、memcache) 代理中间件中的应用

    https://xie.infoq.cn/article/2ee961483c66a146709e7e861 关于作者 前滴滴出行技术专家,现任 OPPO 文档数据库 mongodb 负责人,负责 o ...

  2. [转帖]kafka压测多维度分析实战

    设置虚拟机不同的带宽来进行模拟压测 ---------kafka数据压测-------------------1.公司生产kafka集群硬盘:单台500G.共3台.日志保留7天.         1. ...

  3. [转帖]linux系统目录结构介绍

    linux的目录结构 Linux系统各个目录的作用 /: 根目录.有且只有一个根目录.所有的东西都是从根目录开始.举个例子:当你在终端里输入"/home",你其实是在告诉服务器,先 ...

  4. [转帖]「更易用的OceanBase」|OceanBase 4.0 一体化安装包 - 把简单留给用户

    https://www.modb.pro/db/565842 1. OceanBase 3.x 版本安装浅谈 我是在 OceanBase 3.1.4 版本的时候开始尝试入手测试的.刚开始 OB 3.x ...

  5. vue3新特性

    值得注意的新特性 1==> 组合式 API 2==> Teleport 3==> 片段 4==> 触发组件选项 5==> createRenderer API 来自 @v ...

  6. 小记录 单选框的注意点 html中字符串拼接 el-upload手动上传 表格跳转 v-for动态添加背景色 控制label标签于文本框之间的间距

    在element-ui中 单选框的v-model的值最好是一个字符串 否者可能不能够进行数据回填哈 单选框 的类型必须是字符串类型哈 在elemnet-ui中 如果你想从A页面拿到B页面中的值 可以有 ...

  7. Git、Github、Gitlab与Gitee

    Git.Github.Gitlab与Gitee之间的关系 Git 是一种版本控制系统,是一个命令,是一种工具,有点像cmd(命令行工具). Github 是一个基于git实现在线代码托管的仓库,向互联 ...

  8. Dto中使用正则校验规则,保证传入数据的正确性

    使用RegularExpression

  9. 【主流技术】实战之 Spring Boot 中集成微信支付(小程序)

    前言 微信支付是企业级项目中经常使用到的功能,作为后端开发人员,完整地掌握该技术是十分有必要的. 以下是经过真实商业项目实践的集成步骤,包括注册流程.调用过程.代码demo(经过脱敏)等,希望我的分享 ...

  10. 大语言模型的预训练[5]:语境学习、上下文学习In-Context Learning:精调LLM、Prompt设计和打分函数设计以及ICL底层机制等原理详解

    大语言模型的预训练[5]:语境学习.上下文学习In-Context Learning:精调LLM.Prompt设计和打分函数(Scoring Function)设计以及ICL底层机制等原理详解 1.I ...