最近看到责任链模式的时候每增加一个处理类,就必须在责任链的实现类中手动增加到责任链中,具体代码基本就是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. 视频格式mkv、mp4、avi、flv、mov、wmv、webm特点和区别

    mkv是一种多媒体封装格式,这个封装格式可把多种不同编码的影像及 16 条或以上不同格式的音频和语言不同的字幕封装到一个 Matroska Media 档内. 它也是其中一种开放原始码的多媒体封装格式 ...

  2. SSM久别遇新坑

    SSM久别遇新坑 久别个锤子,也就几天没看,改bug改到怀疑人生 maven的父子模块问题 众所周知,用maven建立一个空的模块,在它之下,将原本的各层次结构分别新建为一个子模块,就能够将各业务进行 ...

  3. netcore3.1 webapi使用signalR

    前言 今天尝试了一下signalR,感觉还不错,因为暂时用不到,就写一篇博文来记录搭建过程,以免以后给忘了,基于官方文档写的,不过官方没有webapi调用例子,就自己写了一下,大神勿喷 使用 1.创建 ...

  4. 使用Qt实现一个必应壁纸客户端

    概要 必应的每日壁纸很好看,但是看不到一周以前的壁纸图片,日前使用python开发了必应壁纸收集站,可惜这样的收集站只能在线浏览,我在想要是有一款软件能够下载每日必应壁纸,并应用到windows的桌面 ...

  5. 《前端运维》一、Linux基础--03Shell基础及补充

    诶诶欸?不是学Linux么?怎么要讲shell了?shell是啥?啥是shell? 别急,我们先简单了解下shell是什么.Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁. ...

  6. [bug] Springboot JPA使用Sort排序时的问题

    参考 https://blog.csdn.net/qq_44039966/article/details/102713779

  7. nginx官方源安装-主配置文件详解

    HTTP相关术语 PV : Page Visit 页面独立浏览量,查看日志生成条数可以看到PV数量. PV全称Page View,中文翻译即页面浏览.其具体的度量方法是从浏览器发出一个对网络服务器的请 ...

  8. win10 中安装 JDK8 以及环境配置

    下载和安装 JDK8 下载 下载地址:https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html 安装 直接双 ...

  9. 11.14 mii-tool:管理网络接口的状态

    mii-tool命令用于查看.管理网络接口,默认情况下网卡的状态是自动协商的,但是有时也会出现不正常的情况,可以使用mii-tool进行调整. mii-tool [option] [interface ...

  10. 统计行数、文件夹个数、文件个数的相关shell命令

    极客君最近做项目,刚好遇到需要统计一些sql文件数量的问题,用到一些实用的shell命令,记录下来,以后万一还能用上呢? 如果在终端不打开文件看到一共多少行,则可以使用wc命令来实现: wc -l [ ...