設計模式之責任鏈模式(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過濾器是如何實現的?)

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