设计模式 -(6)责任链模式(Chain Of Responsibility pattern)
前言
之前做过一个这样的需求:在客户办理业务时对客户信息进行校验,如黑名单校验,坏账校验,如果校验失败则不允许客户继续办理业务。
针对这种情况,首先想到的便是直接在一个方法中完成所有的校验,比如:
public String check(){
if(校验黑名单失败){
return "客户在黑名单中!";
}
if(校验坏账失败){
return "客户存在坏账没处理!";
}
return "校验成功!";
}
如果逻辑简单,代码较少,这样处理未尝不可,但是如果每个校验都有自己单独的逻辑,并且代码量较多,而且有时候需要按照不同的顺序来处理,比如先校验坏账后校验黑名单,那么就必须改动 check 中的代码来达到目的,显然这样不合适
而这种业务非常适合用责任链模式来处理,比如 黑名单 专门使用黑名单校验类来处理,坏账 专门使用坏账的校验类来处理,将这些校验对象放入一个校验数组(校验链)中,依次对客户信息进行不同的校验处理,后续如果有新的校验规则直接加入校验链中即可,并且校验链可以自由调整校验规则的顺序,这种方式明显比直接改 check 方法更加灵活
举例说明
举个过滤字符串中的英文字母,空格,html 标签的例子
定义过滤接口 StrFilter :
public interface StrFilter {
String doFilter(String str);
}
过滤空格的类:
public class BlankFilter implements StrFilter{
@Override
public String doFilter(String str) {
return str.replaceAll("\\s+","");
}
}
过滤英文字母的类:
public class EnglishLetterFilter implements StrFilter{
@Override
public String doFilter(String str) {
return str.replaceAll("[A-Za-z]","");
}
}
过滤器链:
/**
* 过滤器链
* 过滤器链中的过滤器存放在 filterList(元素类型为 StrFilter) 中
* addFilter 方法允许 一个 StrFilterChain 添加另外一个 StrFilterChain,因为 StrFilterChain 继承了 StrFilter
* doFilter 执行时,如果 StrFilter 是具体的过滤器,则调用具体的过滤器的 doFilter 方法,
* 如果 StrFilter 是过滤器链,则调用一系列的过滤器 doFilter 方法
*/
public class StrFilterChain implements StrFilter {
private List<StrFilter> filterList = new ArrayList<>();
/**
* 返回 StrFilterChain 对象可以让使用者重复调用 addFilter 方法
* 比如 strFilterChain.addFilter(new BlankFilter())
* .addFilter(new EnglishLetterFilter());
* @param strFilter
* @return
*/
public StrFilterChain addFilter(StrFilter strFilter) {
filterList.add(strFilter);
return this;
}
@Override
public String doFilter(String str) {
for (int i = 0; i < filterList.size(); i++) {
StrFilter strFilter = filterList.get(i);
str = strFilter.doFilter(str);
}
return str;
}
}
测试:
public class StrTest {
public static void main(String[] args) {
String str = "今 天Today是2022年5月26日!";
StrFilterChain strFilterChain = new StrFilterChain();
strFilterChain.addFilter(new BlankFilter()).addFilter(new EnglishLetterFilter());
String result = strFilterChain.doFilter(str);
System.out.println(result);
}
}
使用空格和英文字母过滤器后,字符串去掉了对应的字符,如下:
今天是2022年5月26日,26,2022
另一种写法
这种方式的特点是在过滤器中调用另外一个过滤器
入参:
public class RequestStr {
public String str;
}
过滤器接口 StrFilter2
public interface StrFilter2 {
void doFilter(StrFilterChain2 strFilterChain, RequestStr requestStr);
}
接口实现类:
public class BlankFilter2 implements StrFilter2 {
@Override
public void doFilter(StrFilterChain2 strFilterChain, RequestStr requestStr) {
System.out.println("BlankFilter begin...");
requestStr.str = requestStr.str.replaceAll("\\s+", "");
strFilterChain.doFilter(strFilterChain, requestStr);
System.out.println("BlankFilter end...");
}
}
public class EnglishLetterFilter2 implements StrFilter2 {
@Override
public void doFilter(StrFilterChain2 strFilterChain, RequestStr requestStr) {
System.out.println("EnglishLetterFilter begin...");
requestStr.str = requestStr.str.replaceAll("[A-Za-z]", "");
strFilterChain.doFilter(strFilterChain, requestStr);
System.out.println("EnglishLetterFilter end...");
}
}
过滤器链 StrFilterChain2
index 表示下一个过滤器在 filterList 中的下标
public class StrFilterChain2 implements StrFilter2 {
private List<StrFilter2> filterList = new ArrayList<>();
private int index = 0;
public StrFilterChain2 addFilter(StrFilter2 strFilter) {
filterList.add(strFilter);
return this;
}
@Override
public void doFilter(StrFilterChain2 strFilterChain, RequestStr requestStr) {
if (filterList.size() <= 0) {
return;
}
if (index == filterList.size()) {
return;
}
StrFilter2 strFilter = filterList.get(index);
index++;
strFilter.doFilter(strFilterChain, requestStr);
}
}
测试:
public class StrTest2 {
public static void main(String[] args) {
RequestStr requestStr = new RequestStr();
requestStr.str = "今 天Today是2022年5月26日!";
StrFilterChain2 strFilterChain = new StrFilterChain2();
strFilterChain.addFilter(new BlankFilter2()).addFilter(new EnglishLetterFilter2());
strFilterChain.doFilter(strFilterChain,requestStr);
System.out.println(requestStr.str);
}
}
如下:日志打印顺序类似 栈 ,BlankFilter begin 在 EnglishLetterFilter begin. 前打印,而 BlankFilter end. 在 EnglishLetterFilter end 后打印
BlankFilter begin...
EnglishLetterFilter begin...
EnglishLetterFilter end...
BlankFilter end...
今天是2022年5月26日!
使用责任链模式的源码
Tomcat Filter
public interface Filter {
default void init(FilterConfig filterConfig) throws ServletException {
}
void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
default void destroy() {
}
}
FilterChain
public interface FilterChain {
void doFilter(ServletRequest var1, ServletResponse var2) throws IOException, ServletException;
}