设计模式之职责链模式
文章目录
前言:在学习一些开源的项目时,遇到很多的职责链模式设计,多租户tenand_id通过过滤器来实现,token验证通过拦截器实现等等;本文参考了极客时间的《设计模式之美》,极客时间相比其他的博客网站比较好的地方在于有很多评论,可以学习到很多东西,安利一下~
一、职责链模式
- 概念:将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求。将这些接收对象串成一条链,并沿着这条链传递这个请求,直到链上的某个接收对象能够处理它为止。
- 标准的职责链模式:链上的处理器顺序执行,有一个处理器可以处理,就终止传递执行
- 变体的职责链模式:链上的处理器会顺序执行,不会终止
- 职责链的两种实现方式
- 链表:只记录
head
和tail
,结合模板方法模式,显式调用下一个处理器,具体处理器只要实现自己的处理逻辑即可。 - 数组列表:将处理器放进一个
list
里,Java
的arraylist
底层就是一个数组,for
循环调用所有的处理器。
- 链表:只记录
注:本文案例用的是数组列表来实现职责链的
二、职责链模式-数组实现案例
- 背景:对网友们评论发表的内容进行敏感词过滤,比如:性、政治、广告之类的过滤
核心:一个filter接口、一个filterChain类
2.1 敏感词接口类
/**
* 敏感词过滤接口
*
* @author zhangzhiguo
* @date 2024/2/27
*/
public interface SensitiveWordFilter {
/**
* 过滤
*
* @param content 内容
* @return 是否继续
*/
boolean doFilter(String content);
}
2.2 敏感词接口实现类-性关键词过滤类
/**
* 两性敏感词过滤器
*
* @author zhangzhiguo
* @date 2024/2/27
*/
public class SexyWordFilter implements SensitiveWordFilter {
@Override
public boolean doFilter(String content) {
System.out.println("两性敏感词过滤开始");
if (content.contains("黄")) {
return Boolean.FALSE;
}
System.out.println("两性敏感词过滤通过");
return Boolean.TRUE;
}
}
2.3 敏感词接口实现类-政治关键词过滤类
/**
* 政治敏感词过滤器
*
* @author zhangzhiguo
* @date 2024/2/27
*/
public class PoliticalWordFilter implements SensitiveWordFilter {
@Override
public boolean doFilter(String content) {
System.out.println("政治敏感词过滤开始");
if (content.contains("党")) {
return Boolean.FALSE;
}
System.out.println("政治敏感词过滤通过");
return Boolean.TRUE;
}
}
2.4 敏感词接口实现类-广告关键词过滤类
/**
* 广告敏感词过滤器
*
* @author zhangzhiguo
* @date 2024/2/27
*/
public class AdsWordFilter implements SensitiveWordFilter {
@Override
public boolean doFilter(String content) {
System.out.println("广告敏感词过滤开始");
if (content.contains("优惠")) {
return Boolean.FALSE;
}
System.out.println("广告敏感词过滤通过");
return Boolean.TRUE;
}
}
2.5 敏感词过滤链类
/**
* 敏感词过滤链
*
* @author zhangzhiguo
* @date 2024/2/27
*/
public class SensitiveWordFilterChain {
// 过滤器数组
private List<SensitiveWordFilter> filters = new ArrayList<>();
public void addFilter(SensitiveWordFilter filter) {
this.filters.add(filter);
}
public boolean filter(String content) {
for (SensitiveWordFilter filter : filters) {
if (!filter.doFilter(content)) {
return false;
}
}
return true;
}
}
2.6 开始测试
// 开始测试
public static void main(String[] args) {
// 方法一:这种方式获得chain每次新增filter都需要修改main方法,不符合开闭原则
// SensitiveWordFilterChain chain = getSensitiveWordFilterChain();
// 方法二:动态获取所有SensitiveWordFilter的接口实现类,不需要手动添加
SensitiveWordFilterChain chain = getSensitiveWordFilterChain2();
if (chain.filter("666")) {
System.out.println("评论发表成功");
} else {
System.out.println("评论发表失败");
}
}
/**
* 动态获取所有SensitiveWordFilter的接口实现类
*
* @return 职责链
*/
private static SensitiveWordFilterChain getSensitiveWordFilterChain2() {
SensitiveWordFilterChain chain = new SensitiveWordFilterChain();
ServiceLoader<SensitiveWordFilter> load = ServiceLoader.load(SensitiveWordFilter.class);
Iterator<SensitiveWordFilter> it = load.iterator();
while (it.hasNext()) {
SensitiveWordFilter filter = it.next();
chain.addFilter(filter);
}
return chain;
}
/**
* 手动 new 职责过滤器的实现方式,不符合开闭原则
*
* @return 职责链
*/
private static SensitiveWordFilterChain getSensitiveWordFilterChain() {
SensitiveWordFilterChain chain = new SensitiveWordFilterChain();
chain.addFilter(new SexyWordFilter());
chain.addFilter(new PoliticalWordFilter());
chain.addFilter(new AdsWordFilter());
return chain;
}
-
注意事项上文方法二:需要在
resources
目录下新建目录:META-INF/services
目录,新建文件:名称为上文过滤接口的全称,不加.java
后缀,如:com.xxxxx.xxxx.chain.SensitiveWordFilter
,然后文件中添加接口实现的类,一行一个(如果没有这个步骤,方法二加载不到类的所有实现类)
-
测试结果
-
发表成功
-
发表失败
-
三、总结
- 看了很多篇职责链的实现文章以及案例,但是百看不如一练,自己动手实现一遍,才能真正理解职责链的设计思想。根据手动new的方式让我体会到了模式的实现流程;自我思考如何模拟框架动态获取所有类以及如何符合开闭原则进行拓展???通过上文方法二实现了。建议大家自己梳理思路,自己动手练习代码。
补充
springboot实现获取某个接口的所有实现类
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* 获取某个接口的所有实现类
*
* @author zhangzhiguo
* @date 2024/2/27
*/
@Component
public class ServiceLocator implements ApplicationContextAware {
/**
* 用于保存接口实现类名及对应的类
*/
private Map<String, SensitiveWordFilter> map;
/**
* 获取应用上下文并获取相应的接口实现类
*
* @param applicationContext
* @throws BeansException
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
//根据接口类型返回相应的所有bean
map = applicationContext.getBeansOfType(SensitiveWordFilter.class);
}
/**
* 获取所有实现集合
*
* @return
*/
public Map<String, SensitiveWordFilter> getMap() {
return map;
}
/**
* 获取对应服务
*
* @param key
* @return
*/
public SensitiveWordFilter getService(String key) {
return map.get(key);
}
}