设计模式之责任链模式(Chain of Responsibility)

责任链模式

用过tomcat或者springMVC的人应该都对过滤器不陌生吧,过滤器的实现就是责任链模式。
且看如何一步一步推到。话不多说,直接上代码。

//这段代码是实现将骂人词汇过滤:
public class Main {
    public static void main(String[] args) {
        Msg msg = new Msg();
        msg.setMsg("djsiaohfioja shit dsjakljf wtf is");
        String r= msg.getMsg();
        r = r.replace("shit","ssss");
        msg.setMsg(r);

        r = r.replace("wtf","what");
        msg.setMsg(r);
        
        System.out.println(msg);
    }
}

class Msg{
    String msg;

    @Override
    public String toString() {
        return "Msg{" +
                "msg='" + msg + '\'' +
                '}';
    }
    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}

以上是两个过滤的方法,当有更多的过滤方法的话,如果来一个就往源码中添加一个,那是极其麻烦的,而且不易拓展。

可以这样修改:

将过滤操作抽象成一个接口

interface Filter{
    void doFilter(Msg m);
}

定义两个过滤器:

class ShitFilter implements Filter{

    @Override
    public void doFilter(Msg m) {
        String r = m.getMsg();
        r = r.replace("shit","ssss");
        m.setMsg(r);
    }
}

class WtfFilter implements Filter{

    @Override
    public void doFilter(Msg m) {
        String r = m.getMsg();
        r = r.replace("wtf","what");
        m.setMsg(r);
    }
}

现在我们只需要在main中:

public class Main {
    public static void main(String[] args) {
        Msg msg = new Msg();
        msg.setMsg("djsiaohfioja shit dsjakljf wtf is");
        new ShitFilter().doFilter(msg);
        new WtfFilter().doFilter(msg);

        System.out.println(msg);
    }
}

这样就实现了过滤的功能,但是此时如果有新的过滤器,还是要添加代码。我们可以将过滤器添加到一个List集合中,这样做是为了之后可以将过滤器定义到配置文件中,而向list中添加元素时,读取配置文件添加,这样就避免了修改源码:

public class Main {
    public static void main(String[] args) {
        List<Filter> filters = new ArrayList<>();
        
        Msg msg = new Msg();
        msg.setMsg("djsiaohfioja shit dsjakljf wtf is");
        
        filters.add(new ShitFilter());
        filters.add(new WtfFilter());
        
        for (Filter filter : filters) {
            filter.doFilter(msg);
        }
        System.out.println(msg);
    }
}

架构类似于:

在这里插入图片描述

此时只是向list中添加,然后遍历执行,其实并不是一条链,只是挨个执行而已,我们将过滤器串成一条链,

定义类,并将list封装到其中,方法包括向链中添加过滤器(add)、从头开始运行这条链(doFilter)、删除过滤器…:

class FilterChain{
    List<Filter> filters = new ArrayList<>();
    public void add(Filter filter)
    {
        filters.add(filter);
    }

    public void doFilter(Msg msg)
    {
        for (Filter filter : filters) {
            filter.doFilter(msg);
        }
    }
    //删除filter等等一系列方法...
}

main中修改为:

public class Main {
    public static void main(String[] args) {

        Msg msg = new Msg();
        msg.setMsg("djsiaohfioja shit dsjakljf wtf is");
        FilterChain filters = new FilterChain();
        filters.add(new ShitFilter());
        filters.add(new WtfFilter());
        filters.doFilter(msg);
        System.out.println(msg);
    }
}

看到这里你或许有疑问,这有什么区别呢,不就是把list装到了FilterChain里了吗,没有区别啊。

区别在什么地方呢?

其实区别很大,当我们有两个FilterChain时,其实是可以将两个链条连在一起的。

在实现之前先做一个小小的修改,因为每次filters都要add,所以我们将这里改为链式编程模式,将FilterChain中的add方法修改,返回FilterChain对象,即返回当前对象本身。

public FilterChain add(Filter filter)
{
    filters.add(filter);
    return this;
}

之前我们如果要添加过滤器:

filters.add(new ShitFilter());
filters.add(new WtfFilter());

现在我们只需:

filters.add(new ShitFilter()).add(new WtfFilter());

是不是感觉简便了许多(o゚v゚)ノ

现在当我们有两条filterchiain时,该怎么连起来呢?首先我们模拟一下多链条:

定义

class FuckFilter implements Filter{

    @Override
    public void doFilter(Msg m) {
        String r = m.getMsg();
        r = r.replace("fuck","****");
        m.setMsg(r);
    }
}
class BitchFilter implements Filter{

    @Override
    public void doFilter(Msg m) {
        String r = m.getMsg();
        r = r.replace("bitch","bbbbb");
        m.setMsg(r);
    }
}

定义两条链:

FilterChain filters = new FilterChain();
filters.add(new ShitFilter())
    .add(new WtfFilter());

FilterChain filters2 = new FilterChain();
filters.add(new FuckFilter())
    .add(new BitchFilter());

filters.doFilter(msg);
filters2.doFilter(msg);

filters与filters2怎么合并呢?

很简单,只需让FilterChain也实现Filter接口

class FilterChain implements Filter{
    List<Filter> filters = new ArrayList<>();
    public FilterChain add(Filter filter)
    {
        filters.add(filter);
        return this;
    }

    public void doFilter(Msg msg)
    {
        for (Filter filter : filters) {
            filter.doFilter(msg);
        }
    }
    //删除filter等等一系列方法...
}

现在我们只需要这样:

FilterChain filters = new FilterChain();
filters.add(new ShitFilter())
    .add(new WtfFilter());

FilterChain filters2 = new FilterChain();
filters.add(new FuckFilter())
    .add(new BitchFilter());

filters.add(filters2);

filters.add(filters2);这样就实现了两个链条连在了一起。

那么它是怎么执行的呢?

这种调用类似于递归,当我们运行filters.doFilter(msg)时,会执行:

//这里是filters的循环,会调用filters2的doFilter
for (Filter filter : filters) {
    filter.doFilter(msg);
}

因为我们的filters2也实现了Filter接口,所以当执行到filters2时,调用filters2的doFilter(msg)方法,这样就会执行filters2中的过滤器:

//这里是filters2的循环,被filters调用
for (Filter filter : filters) {
    filter.doFilter(msg);
}

这样我们不论写多少过滤器,都可以链成一条

我们再进一步改进,上面的过滤器都是一下子全部执行完,但是现在需要的是,思考这种场景:当执行到一个过滤器时,如果要是发现异常,就不要再往下再执行了,该怎么实现呢?

我们只需要将Filter中的doFilter方法返回值改为boolean类型

interface Filter{
    boolean doFilter(Msg m);
}

class ShitFilter implements Filter{

    @Override
    public boolean doFilter(Msg m) {
        String r = m.getMsg();
        r = r.replace("shit","ssss");
        m.setMsg(r);
        //这里可以做判断,如果不想让他往下执行,返回false.
        return true;
    }
}
//其他过滤器一样

然后我们修改FilterChain中的doFilter方法:

public boolean doFilter(Msg msg)
{
    for (Filter filter : filters) {
        if (!filter.doFilter(msg)) return false;
    }
    return true;
}

当其中一环返回false,那就返回false,不往下执行了。

思考题:servlet中的过滤器是这样的:

在这里插入图片描述

如何实现过滤器,使其处理request时是f1,f2,f3,f4的顺序,处理response时的顺序是f4,f3,f2,f1?
答案:设计模式之责任链模式思考题(servlet中filter过滤器是如何实现的?)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章