最近看到责任链模式的时候每增加一个处理类,就必须在责任链的实现类中手动增加到责任链中,具体代码基本就是list.add(new FilterImpl()),可以看到每次增加一个处理类,就必须添加一行上面的代码,不符合开闭原则(面向修改关闭,面向扩展开放)。于是想到了Java的SPI机制,可以实现插拔式组件,而Java自带的SPI机制是寻找借口的所有实现类,虽然一直被诟病,但是在本次的插拔式中,它却成了一个优点,因为我们需要把所有的实现类都放入处理器链中。

类图如下:

上代码,嘻嘻!

 1 package com.liekkas.spi.responsibility_chain_model;
2
3 import lombok.Data;
4
5 /**
6 * description 被处理的类
7 *
8 * @author liekkas 2020/12/02 23:33
9 */
10 @Data
11 public class Receipt {
12 public Receipt(String code,String message){
13 this.code = code;
14 this.message = message;
15 }
16 private String code;
17 private String message;
18 }
 1 package com.liekkas.spi.responsibility_chain_model;
2
3 /**
4 * description 处理器接口
5 *
6 * @author liekkas 2020/12/02 23:30
7 */
8 public interface Filter {
9
10 /**
11 * 处理方法
12 * @param receipt receipt
13 * @param filterChain filterChain
14 */
15 void handle(Receipt receipt,FilterChain filterChain);
16 }
 1 package com.liekkas.spi.responsibility_chain_model;
2
3 /**
4 * description 处理器链接口
5 *
6 * @author liekkas 2020/12/02 23:31
7 */
8 public interface FilterChain {
9
10 /**
11 * 处理方法
12 * @param receipt receipt
13 */
14 void handleReceipt(Receipt receipt);
15 }
 1 package com.liekkas.spi.responsibility_chain_model.impl;
2
3 import com.liekkas.spi.responsibility_chain_model.Filter;
4 import com.liekkas.spi.responsibility_chain_model.FilterChain;
5 import com.liekkas.spi.responsibility_chain_model.Receipt;
6
7 /**
8 * description 具体的处理器1
9 *
10 * @author liekkas 2020/12/02 23:35
11 */
12 public class FilterImpl1 implements Filter {
13
14 @Override
15 public void handle(Receipt receipt, FilterChain filterChain) {
16 if ("test001".equals(receipt.getCode())) {
17 System.out.println("我是Filter1,我能处理test001,已经处理" + receipt.getMessage());
18 }
19 //自己处理不了该回执就往下传递
20 else {
21 filterChain.handleReceipt(receipt);
22 }
23 }
24 }
 1 package com.liekkas.spi.responsibility_chain_model.impl;
2
3 import com.liekkas.spi.responsibility_chain_model.Filter;
4 import com.liekkas.spi.responsibility_chain_model.FilterChain;
5 import com.liekkas.spi.responsibility_chain_model.Receipt;
6
7 /**
8 * description 具体的处理器2
9 *
10 * @author liekkas 2020/12/02 23:35
11 */
12 public class FilterImpl2 implements Filter {
13
14 @Override
15 public void handle(Receipt receipt, FilterChain filterChain) {
16 if ("test002".equals(receipt.getCode())) {
17 System.out.println("我是Filter2,我能处理test002,已经处理" + receipt.getMessage());
18 }
19 //自己处理不了该回执就往下传递
20 else {
21 filterChain.handleReceipt(receipt);
22 }
23 }
24 }
 1 package com.liekkas.spi.responsibility_chain_model.impl;
2
3 import com.liekkas.spi.responsibility_chain_model.Filter;
4 import com.liekkas.spi.responsibility_chain_model.FilterChain;
5 import com.liekkas.spi.responsibility_chain_model.Receipt;
6
7 /**
8 * description 具体的处理器3
9 *
10 * @author liekkas 2020/12/02 23:35
11 */
12 public class FilterImpl3 implements Filter {
13
14 @Override
15 public void handle(Receipt receipt, FilterChain filterChain) {
16 if ("test003".equals(receipt.getCode())) {
17 System.out.println("我是Filter3,我能处理test003,已经处理" + receipt.getMessage());
18 }
19 //自己处理不了该回执就往下传递
20 else {
21 filterChain.handleReceipt(receipt);
22 }
23 }
24 }
 1 package com.liekkas.spi.responsibility_chain_model.impl;
2
3 import com.liekkas.spi.responsibility_chain_model.Filter;
4 import com.liekkas.spi.responsibility_chain_model.FilterChain;
5 import com.liekkas.spi.responsibility_chain_model.Receipt;
6 import com.liekkas.spi.responsibility_chain_model.container.ReceiptHandlerContainer;
7
8 import java.util.List;
9
10 /**
11 * description 处理器链默认的实现类
12 *
13 * @author liekkas 2020/12/02 23:38
14 */
15 public class DefaultFilterChainImpl implements FilterChain {
16
17 /**
18 * 记录当前处理者位置
19 */
20 private int index = 0;
21 /**
22 * 处理器集合
23 */
24 private static final List<Filter> FILTER_LIST;
25
26 static {
27 //从容器中获取处理器对象
28 FILTER_LIST = ReceiptHandlerContainer.getReceiptHandlerList();
29 }
30
31 @Override
32 public void handleReceipt(Receipt receipt) {
33 if (FILTER_LIST != null && FILTER_LIST.size() > 0) {
34 if (index < FILTER_LIST.size()) {
35 Filter filter = FILTER_LIST.get(index++);
36 filter.handle(receipt, this);
37 }
38 }
39 }
40 }
 1 package com.liekkas.spi.responsibility_chain_model.container;
2
3 import com.liekkas.spi.responsibility_chain_model.Filter;
4
5 import java.util.ArrayList;
6 import java.util.List;
7 import java.util.ServiceLoader;
8
9 /**
10 * description 装在过滤器的容器,采用SPI实现,当添加过滤器的时候只需按照SPI的格式要求,便可自动装载,
11 * 无需其他操作,即实现了插拔式,即插即用。
12 *
13 * @author liekkas 2020/12/02 23:43
14 */
15 public class ReceiptHandlerContainer {
16 private ReceiptHandlerContainer() {
17 }
18
19 public static List<Filter> getReceiptHandlerList() {
20 // SPI机制,寻找所有的实现类
21 ServiceLoader<Filter> filtersImplements = ServiceLoader.load(Filter.class);
22 List<Filter> receiptHandlerList = new ArrayList<>();
23 //把找到的所有的Filter的实现类放入List中
24 for (Filter filtersImplement : filtersImplements) {
25 receiptHandlerList.add(filtersImplement);
26 }
27 return receiptHandlerList;
28 }
29 }

最后在resources目录下建一个文件名为com.liekkas.spi.responsibility_chain_model.Filter的文件,文件名即是接口的全限定接口名,完整的目录如下:

src/main/resources/META-INF/services/com.liekkas.spi.responsibility_chain_model.Filter

文件内容为:

com.liekkas.spi.responsibility_chain_model.impl.FilterImpl1
com.liekkas.spi.responsibility_chain_model.impl.FilterImpl2
com.liekkas.spi.responsibility_chain_model.impl.FilterImpl3

下面我们就建一个客户端的测试类,来看一下效果,激动人心的时刻到了,嘿嘿嘿

package com.liekkas.spi.responsibility_chain_model;

import com.liekkas.spi.responsibility_chain_model.impl.DefaultFilterChainImpl;

import java.util.ArrayList;
import java.util.List; /**
* description 测试客户端
*
* @author liekkas 2020/12/02 23:52
*/
public class Client {
public static void main(String[] args) {
//模拟回执
List<Receipt> receiptList = ReceiptBuilder.generateReceiptList(); for (Receipt receipt : receiptList) {
//回执处理链对象
FilterChain receiptHandleChain = new DefaultFilterChainImpl();
receiptHandleChain.handleReceipt(receipt);
}
} static class ReceiptBuilder{
public static List<Receipt> generateReceiptList(){
List<Receipt> resultList = new ArrayList<>();
resultList.add(new Receipt("test001","测试消息one"));
resultList.add(new Receipt("test002","测试消息two"));
resultList.add(new Receipt("test003","测试消息three"));
return resultList;
}
}
}

执行的结果如下:

小结:通过本次的学习,不仅对责任链模式有了更深的理解也对SPI机制有了更深的理解,同时两者的结合实现了插拔式,也让我对软件的设计原则有了更深一步的认识!

利用SPI机制实现责任链模式中的处理类热插拔的更多相关文章

  1. 【设计模式】 模式PK:观察者模式VS责任链模式

    1.概述 为什么要把观察者模式和责任链模式放在一起对比呢?看起来这两个模式没有太多的相似性,真没有吗?回答是有.我们在观察者模式中也提到了触发链(也叫做观察者链)的问题,一个具体的角色既可以是观察者, ...

  2. 观察者模式 VS 责任链模式

    为什么要把观察者模式和责任链模式放在一起对比呢?这两个模式没有太多的相似性呀,真没有嘛?有相似性,我们在观察者模式中也提到了触发链(也叫做观察者链)的问题,一个具体的角色既可以是观察者,也可以是被观察 ...

  3. Netty中的责任链模式

    适用场景: 对于一个请求来说,如果有个对象都有机会处理它,而且不明确到底是哪个对象会处理请求时,我们可以考虑使用责任链模式实现它,让请求从链的头部往后移动,直到链上的一个节点成功处理了它为止 优点: ...

  4. Activiti工作流学习笔记(四)——工作流引擎中责任链模式的建立与应用原理

    原创/朱季谦 本文需要一定责任链模式的基础,主要分成三部分讲解: 一.简单理解责任链模式概念 二.Activiti工作流里责任链模式的建立 三.Activiti工作流里责任链模式的应用 一.简单理解责 ...

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

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

  6. 责任链模式Scala的7种实现

    责任链模式是经典的GoF 23种设计模式之一,也许你已经了解这种模式.不管你是否熟悉,建议读者在阅读本文之前,不妨先思考下面三个问题: (1) 如何用多种风格迥异的编程范式来实现责任链模式? (2) ...

  7. 详解java设计模式之责任链模式

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt175 从击鼓传花谈起 击鼓传花是一种热闹而又紧张的饮酒游戏.在酒宴上宾客依次 ...

  8. 责任链模式 职责链模式 Chain of Responsibility Pattern 行为型 设计模式(十七)

    责任链模式(Chain of Responsibility Pattern) 职责链模式 意图 使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系 将这些对象连接成一条链,并沿着这 ...

  9. Android事件分发与责任链模式

    一.责任链模式 责任链模式是一种行为模式,为请求创建一个接收者的对象链.这样就避免,一个请求链接多个接收者的情况.进行外部解耦.类似于单向链表结构. 优点: 1. 降低耦合度.它将请求的发送者和接收者 ...

随机推荐

  1. Codeforces Round #694 (Div. 2)

     A. Strange Partition 题意:就是求最小和最大的bi/x向上取整的和. 思路:见题解:https://blog.csdn.net/qq_45900709/article/detai ...

  2. 图扑软件正式加入腾讯智维生态发展计划,智能 IDC 开启数字经济新征程

    4 月 23 日,主题为<智汇科技,维新至善>的腾讯数据中心智维技术研讨会在深圳胜利召开,发布了腾讯智维 2.0 技术体系,深度揭秘了智维 2.0 新产品战略和技术规划.图扑软件(High ...

  3. Linux下线程pid和tid

    #include <stdio.h> #include <pthread.h> #include <sys/types.h> #include <sys/sy ...

  4. CSS3文本样式

    目录 文本阴影 text-shadow 文本轮廓 text-outline 文本换行 word-break normal break-all keep-all word-wrap 新文本属性 text ...

  5. [bug] SSM项目:Cannot load driver class: com.mysql.jdbc.Driver

    检查pom文件,mysql包部分为: <dependency> <groupId>mysql</groupId> <artifactId>mysql-c ...

  6. Linux中find命令用法全汇总,看完就没有不会用的!

    Linux中find命令用法全汇总,看完就没有不会用的! 中琦2513 马哥Linux运维 2017-04-10   糖豆贴心提醒,本文阅读时间7分钟 Linux 查找命令是Linux系统中最重要和最 ...

  7. 实例:使用playbook实现httpd安装、配置、以及虚拟主机的配置

    一.安装环境配置 1.在控制节点给受控主机配置本地仓库文件 [root@ansible ~]# vim /etc/yum.repos.d/dvd.repo [AppStream] name=appst ...

  8. Linux中的防火墙

    firewalld 一.防火墙安全概述 firewalld支持命令行也支持GUI设置,相对于iptables,firewalld配置更加的方便.在底层的命令都是iptables, firewalld ...

  9. UCOSII中的主栈扩展

    听陈*均说 UCOSII源代码中有这样的机制 当某个TASK的栈不够用.访问越界时 会自动调用系统生成的备用扩展栈区 但这样也有风险 备用扩展栈区如果被击穿则会造成更大的错误,可能会导致程序跑飞,如果 ...

  10. Spring IoC容器 XML 配置与加载

    IoC 容器 XML 配置文件 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns=&qu ...