责任链设计模式

责任链模式用于弱化请求发生者和请求处理者之间的关系。当多个对象都可以对请求进行处理,但不同的对象能处理的请求类型不同时,可以通过指向另一个对象的引用把这些对象连成一条责任链。当 Client 发出一个请求时,并不知道具体由哪个对象进行处理,它看到的只是一条责任链,将请求直接交给责任链,请求会在责任链中传递,直到找到一个能够进行处理的对象或者遍历结束找不到一个能够处理的对象为止。Java 语言中的异常处理机制就是责任链模式的一个典型应用例子。另外一些框架源码中也大量使用到责任链模式,比如SpringMVC中对拦截器的处理,Servelet中过滤器链的处理,SpringSecurity/shiro中认证逻辑的处理,以及Netty中piple中ChannelHandler的处理

实现功能:模拟Servelet中的请求过滤器链实现对输入字符串的过滤:对输入的字符分别过滤出水果蔬菜字符串

1.if-else方式

最简单的方式就是通过if-elae来实现,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
public static void main(String[] args) {
String data = "水果蔬菜鸡蛋和肉都很好吃";

if (data.contains("水果")) {
data = data.replaceAll("水果", "");
}
if (data.contains("蔬菜")) {
data = data.replaceAll("蔬菜", "");
}

System.out.println(data);
}

以上方式虽然可以实现功能,但是有如下缺点:

  1. 不满足开闭原则如果增加过滤逻辑,则需要增加if-else
  2. 调整顺序的话需要修改代码

    2.责任链设计模式方式

    接下来通过责任链设计模式来改造,核心代码如下:
  • 请求对象(被过滤对象)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    /**
    * @author haopeng
    * @date 2021-07-05 19:52
    */
    public class Request {

    /**
    * 请求数据
    */
    private String data;

    public Request() {

    }

    public Request(String data) {
    this.data = data;
    }

    public String getData() {
    return data;
    }

    public void setData(String data) {
    this.data = data;
    }
    }
  • 过滤器链

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    /**
    * 过滤器链
    * @author haopeng
    * @date 2021-07-05 19:51
    */
    /**
    * 过滤器链
    * @author haopeng
    * @date 2020-08-05 19:51
    */
    public class FilterChain {


    private List<Filter> filters = new ArrayList<>();

    private int index = 0;

    public void addFilter(Filter filter) {
    this.filters.add(filter);
    }

    public Request doFilter(Request request) {

    //执行终止条件
    if (index >= filters.size()) {
    return request;
    }
    Filter filter = filters.get(index);
    index++;

    //类似递归执行
    return filter.doFilter(request, this);
    }
    }

  • 过滤器接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    /**
    * @author haopeng
    * @date 2021-07-05 19:51
    */
    public interface Filter {
    Request doFilter(Request request, FilterChain filterChain);

    default Request filterByKeyWord(Request request, String word) {
    StringBuilder sb = new StringBuilder(request.getData());
    while (true) {
    int index = sb.indexOf(word);
    if (index != -1) {
    for (int i = 0; i < word.length(); i++) {
    sb.deleteCharAt(index);
    }
    } else {
    break;
    }
    }
    return new Request(sb.toString());
    }

    }
  • 水果过滤器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    /**
    * @author haopeng
    * @date 2021-07-05 20:31
    */
    public class FruitFilter implements Filter {
    @Override
    public Request doFilter(Request request, FilterChain filterChain) {

    request = filterByKeyWord(request, "水果");

    return filterChain.doFilter(request);

    }
    }
  • 蔬菜过滤器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    /**
    * @author haopeng
    * @date 2021-07-05 20:31
    */
    public class VegetablesFilter implements Filter {
    @Override
    public Request doFilter(Request request, FilterChain filterChain) {

    request = filterByKeyWord(request, "蔬菜");

    return filterChain.doFilter(request);

    }
    }
  • 测试类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    /**
    * @author haopeng
    * @date 2021-07-05 20:43
    */
    public class Client {

    public static void main(String[] args) {
    Request request = new Request("水果蔬菜鸡蛋和肉都很好吃");

    FilterChain filterChain = new FilterChain();

    //添加过滤器
    filterChain.addFilter(new FruitFilter());
    filterChain.addFilter(new VegetablesFilter());

    //执行

    request = filterChain.doFilter(request);
    System.out.println(request.getData());
    }
    }
  • 执行结果

    1
    鸡蛋和肉都很好吃

类图如下:

责任链模式的优点

1.如果要加一些过滤的关键字,只需要加一个过滤的类,然后再添加到过滤器的集合中,不需要对原来的代码进行侵入式的开发。符合开闭原则。

2.执行顺序要变更的话,也不需要侵入式的改代码,只需要改一下添加过滤器的顺序。比如在Servlet中,过滤器的顺序就是按xml文件定义的顺序。或通过@Order注解完成