设计模式之责任链 Chain Of Responsibility

简介: 设计模式之责任链 Chain Of Responsibility

所谓设计模式就是把简单的问题复杂化,哪部分的代码会变化就封装哪部分

我们来看一个场景:我们的网站上会提供一些留言面板,用户可以自由输入留言,所以我们需要对这些留言进行限制,防止黑客攻击和一些不良言论。

按照传统的方式,代码如下:

public static void main(String[] args) {
        Msg msg = new Msg();
        msg.setMsg("大家好:!!!<script> </script>,欢迎大家访问CodeSavant.com,希望大家能摆脱996");
        String s = msg.getMsg();
        s = s.replace("<", "").replace(">", "");
        msg.setMsg(s);
        s = s.replaceAll("996","965");
        System.out.println(s);
    }

这显然不适合扩展,我们在替换字符串的这个处理过程是一步接一步的,于是就可以使用到责任链设计模式

责任链设计模式必备的要素:

  • 抽象处理者
  • 具体处理者
  • 请求

首先,我们来定义一个抽象处理者

public interface Filter {
    void doFilter(Msg msg);
}

接着我们定义具体的处理者来处理

public class BadFilter implements Filter{
    @Override
    public void doFilter(Msg msg) {
        String s = msg.getMsg();
        s = s.replaceAll("996","965");
        msg.setMsg(s);
    }
}
public class CodeFilter implements Filter{
    @Override
    public void doFilter(Msg msg) {
        String s = msg.getMsg();
        s = s.replace("<", "").replace(">", "");
        msg.setMsg(s);
    }
}

最后我们在客户端代码中调用Filter进行处理

public static void main(String[] args) {
        Msg msg = new Msg();
        msg.setMsg("大家好:!!!<script> </script>,欢迎大家访问CodeSavant.com,希望大家能摆脱996");
        new BadFilter().doFilter(msg);
        new CodeFilter().doFilter(msg);
        System.out.println(msg);
    }

执行之后可以看到,与我们直接写的代码执行结果一致。但是这还不够完美,因为我们需要一行一行的去调用,这显然不合理。因此我们要进行优化

我们引入一个新的类,即调用链。通过调用链我们可以将每个处理者添加到其中,这样可以实现顺序执行

public class FilterChain {
    List<Filter> filterList = new ArrayList<>();
    public FilterChain addFilter(Filter filter){
        filterList.add(filter);
        return this;
    }
    public void doFilter(Msg msg){
        for (Filter f : filterList) {
            f.doFilter(msg);
        }
    }
}

可以看到,调用链持有处理者的列表,也提供了一个doFiilter方法来执行操作。并且提供了一个addFilter来进行添加处理者,这里可以看到我们每次添加完会将当前的对象返回回去,这个会在建造者模式中谈到。

我们看一下修改完了之后的客户端代码

public static void main(String[] args) {
        Msg msg = new Msg();
        msg.setMsg("大家好:!!!<script> </script>,欢迎大家访问CodeSavant.com,希望大家能摆脱996");
        FilterChain filterChain = new FilterChain();
        filterChain.addFilter(new BadFilter()).addFilter(new CodeFilter());
        filterChain.doFilter(msg);
        System.out.println(msg);
    }

很明显方便了很多。

但是仍然有问题,假设现在又有一个新的调用链,我们没法将两个调用链连接起来。于是有以下的解决方案:

我们让FilterChain本身也去实现Filter接口,即FilterChain本身可以看做一个Filter,接下来在调用的时候只需要把他本身当做Filter合并在一起就行。我们来看代码:

public class FilterChain implements Filter{...}
    public static void main(String[] args) {
        Msg msg = new Msg();
        msg.setMsg("大家好:!!!<script> </script>,欢迎大家访问CodeSavant.com,希望大家能摆脱996");
        FilterChain f1 = new FilterChain();
        f1.addFilter(new BadFilter()).addFilter(new CodeFilter());
        FilterChain f2 = new FilterChain();
        f2.addFilter(new HappyFilter()).addFilter(new ByeFilter());
        f1.addFilter(f2);
        f1.doFilter(msg);
        System.out.println(msg);
    }

可以看到,在以上的例子中,我们将f2本身作为一个Filter全部添加进了f1

那么此时我们可以知道:f1中包含了3个Filter的对象:BadFilter``CodeFilter``f2

f2其本身又包含了两个Filter对象HappyFilter``ByeFilter

这样我们在调用f1.doFilter(msg)的时候,会按照遍历数组的顺序,首先执行f1BadFilter``CodeFilter的doFilter方法,随后在调用到 f2的时候,由于f2其本身也是一个FilterChain,他也是含有自己的数组引用的,于是会调用他的doFilter方法,这样就会随着调用HappyFilter``ByeFilter的doFilter方法了,有点类似递归的一个结构。

以上就是一个基本的实现。但是如果我们现在又会有新的要求:处理到某个Filter节点的时候就终止调用。

于是我们需要在Filter接口的doFilter方法提供一个返回值,当他返回为false的时候,我们终止整个链条的调用

我们看一下代码:

public interface Filter {
    boolean doFilter(Msg msg);
}
public class FilterChain implements Filter{
        public boolean doFilter(Msg msg){
        for (Filter f : filterList) {
            if (!f.doFilter(msg)) return false;
        }
        return true;
    }
}

我们将BadFilter的返回值改为false,这样就只执行这一个就不会接着执行了。

原因就在于if (!f.doFilter(msg)) return false; 在这一行代码里面,我们判断如果执行的返回结果是false,那么就直接return false 终止了循环

同理如果这时候f 引用本身指向的对象也是个 FilterChain,根据我们上面的分析,这个时候f里面也会循环执行doFilter,那么只要他里面有一个方法执行的结果返回了false,那么整个循环就返回了false,同样的会返回到外层,这样就完成了递归调用。

最后我们来讲解一下在Java的Servlet中是如何使用责任链模式Filter的

我们先来了解一下背景,在JavaEE中,我们经常需要处理Request和Response对象,并且我们是先处理Request,然后再返回处理Response的。即Request是正向处理先进先出,Response是反向处理后进先出。

我们先来简单的定义一下Request和Response对象

public class Request {
    String msg;
}
public class Response {
    String msg;
}

接下来我们在之前的代码的基础上,修改一下FilterChain的代码

public class FilterChain {
    List<Filter> filterList = new ArrayList<>();
    int index = 0;
    public FilterChain addFilter(Filter filter){
        filterList.add(filter);
        return this;
    }
    public boolean doFilter(Request request,Response response){
        //判断是否执行完
        if (index == filterList.size()) return false;
        //如果没执行完,取出当前的filter,接着向下执行
        Filter f = filterList.get(index);
        index++;
        return f.doFilter(request,response,this);
    }
}

这里可以看到,我们添加了一个index属性,用来控制FilterChain的调用顺序。并且在doFilter方法里面,必须要传入两个参数。每次调用完一个Filter之后,将索引的值向后移动一位。并且我们也无需再实现Filter接口了,这也是为了精简代码。

接下来看一下Filter

public interface Filter {
    boolean doFilter(Request request,Response response,FilterChain filterChain);
}

基本没有什么变化,最大的区别就是我们持有了FilterChain,这是为了在具体的实现类中进行顺序调用

我们来看一下具体的实现类

public class CodeFilter implements Filter {
    @Override
    public boolean doFilter(Request request,Response response,FilterChain filterChain) {
        request.msg = request.msg.replace("<","").replace(">","");
        filterChain.doFilter(request,response);
        response.msg += "----调用了CodeFilter";
        return true;
    }
}
public class BadFilter implements Filter {
    @Override
    public boolean doFilter(Request request,Response response,FilterChain filterChain) {
        request.msg = request.msg.replace("996","965");
        filterChain.doFilter(request,response);
        response.msg += "----调用了BadFilter";
        return true;
    }
}

我们可以看到,在具体的实现类中,我们先是处理了Request,然后接着顺序调用FilterChain的doFilter方法,最后才处理Response

我们来看一下客户端代码,并分析一下整个的执行过程

public static void main(String[] args) {
        Request request = new Request();
        request.msg = "大家好:!!!<script> </script>,欢迎大家访问CodeSavant.com,希望大家能摆脱996";
        Response response = new Response();
        response.msg = "";
        FilterChain filterChain = new FilterChain();
        filterChain.addFilter(new CodeFilter()).addFilter(new BadFilter());
        //.addFilter(new ByeFilter()).addFilter(new HappyFilter())
        filterChain.doFilter(request,response);
        System.out.println(request);
        System.out.println(response);
    }

我们添加了CodeFilter和BadFilter

那么此时FilterChain的filterList中包含的两个对象就是CodeFilter、BadFilter

首先我们调用了filterChain.doFilter(request,response);这行代码

这时候index才刚刚初始化,所以他的值显然为0

所以我们会从filterList中取出CodeFilter的对象,然后再将index++这时候index的值就已经变成了1

随之调用CodeFilter的doFilter方法

先处理了Request,然后我们可以看到又调用了filterChain.doFilter(request,response);

这里我们如果不接着调用filterChain的话,整个链条就断掉了,因为我们在执行完CodeFilter的方法之后,并不会往后接着走,这主要是因为我们将调用顺序的控制权由FilterChain交给了具体的Filter本身,我们并没有在FilterChain中使用for循环来调用。

接下来划重点:这时我们又进到了FilterChain中,这时候index的值已经是1了,所以会取出BadFilter的对象,接着使index++,那么此时index的值就会变成2了。

紧接着继续调用BadFilter的doFilter方法,先处理Request,然后接着调用filterChain.doFilter(request,response);但是此时由于index的值已经是2了,这时候filterChain会直接返回,不会接着执行了。随之就会处理BadFilter中的Response。那么此时BadFilter的doFilter也处理完成了。我们知道BadFilter是由CodeFilter先调用了filterChain的doFilter方法,然后再调用到了BadFilter,所以这时候随着BadFilter的执行完毕返回,CodeFilter中filterChain.doFilter(request,response);这行代码也执行完毕返回了。那么接着就会向下去执行处理CodeFilter中的Response

所以在整个的处理流程中,顺序是这样的:CodeFilter处理Request、CodeFilter调用FilterChain、BadFilter处理Request、BadFilter调用FilterChain执行直接返回、BadFilter处理Response、BadFilter处理完成返回、CodeFilter处理Response、执行完毕。

这样就实现了Request正向,Response反向

目录
相关文章
|
2月前
|
设计模式 Java
常用设计模式(工厂方法,抽象工厂,责任链,装饰器模式)
有关设计模式的其他常用模式请参考 单例模式的实现 常见的设计模式(模板与方法,观察者模式,策略模式)
43 2
|
2月前
|
设计模式
【设计模式】张一鸣笔记:责任链接模式怎么用?
【设计模式】张一鸣笔记:责任链接模式怎么用?
27 1
|
2月前
|
设计模式 Java
【设计模式】JAVA Design Patterns——Chain of responsibility(责任链模式)
【设计模式】JAVA Design Patterns——Chain of responsibility(责任链模式)
|
2月前
|
设计模式 Java Spring
责任链设计模式详解
该内容主要介绍了如何使用Java实现责任链模式。
40 4
|
2月前
|
设计模式 算法 调度
行为型设计模式:模板设计模式/观察者设计模式/策略设计模式/责任链设计模式
行为型设计模式:模板设计模式/观察者设计模式/策略设计模式/责任链设计模式
38 0
|
2月前
|
设计模式
二十三种设计模式全面解析-职责链模式(Chain of Responsibility Pattern):解放代码责任链,提升灵活性与可维护性
二十三种设计模式全面解析-职责链模式(Chain of Responsibility Pattern):解放代码责任链,提升灵活性与可维护性
|
7月前
|
设计模式 Java 应用服务中间件
认真学习设计模式之职责链模式((Chain of Responsibility Pattern)
认真学习设计模式之职责链模式((Chain of Responsibility Pattern)
70 0
|
9月前
|
设计模式 Java 数据库连接
JAVA设计模式8:装饰模式,动态地将责任附加到对象上,扩展对象的功能
JAVA设计模式8:装饰模式,动态地将责任附加到对象上,扩展对象的功能
|
9月前
|
设计模式 JavaScript Java
设计模式17 - 责任链模式【Chain of Responsibility Pattern】
设计模式17 - 责任链模式【Chain of Responsibility Pattern】
30 0
|
5天前
|
设计模式 存储 算法
设计模式学习心得之五种创建者模式(2)
设计模式学习心得之五种创建者模式(2)
12 2