開發

這就是『責任鏈模式』?

廣告
廣告

前言

只有光頭才能變強。

文本已收錄至我的GitHub精選文章,歡迎Star:https://github.com/ZhongFuCheng3y/3y

最近在看項目代碼的時候發現「責任鏈模式」,于是想花點時間來寫寫什么是責任鏈模式。

不知道大家是怎么學習設計模式的,一般我都是用到的時候,或者接觸到的時候才會去學。否則感覺學完就很容易就忘掉了,不能理解為什么要使用設計模式(因為沒有真實的場景給我去使用)。

在之前我已經更新說幾篇設計模式的文章了,我覺得寫得「還行」,有興趣的同學可以到我的GitHub上,關鍵字搜索「設計模式」,就能找到對應的文章。

不得不說,我現在負責項目的代碼我常常會感嘆:這代碼怎么這么騷啊!項目里邊用到了很多的設計模式,在最開始看的時候會很費勁(因為之前沒學),但維護起來是真的方便。

一、什么是責任鏈模式?

在說責任鏈模式之前,我們先來聊聊「過濾器」。

過濾器相信大家都肯定學過了,在最開始學Servlet的時候我們會學到Filter。等學到Struts2的時候,我們會學到Interceptor。等學到SpringMVC的時候,我們會學到HandlerInterceptor

但無論學哪個框架,我們發現是最終其實它還是做Filter這么一件事。說白了就是:

  • 把所有的過濾器都放在FilterChain里邊,依次執行每個過濾器。

在我的GitHub對Filter,HandlerInterceptor,Interceptor都有講到,如果想要復習的同學不妨進去搜索關鍵字「過濾器」「Struts2」「SpringMVC

為什么看責任鏈模式要聊「過濾器」呢?后面會講到,不要著急。

1.1 干掉敖丙和雞蛋

舉個例子:把我們的正常請求想象成一堆的雜物,里邊有米豆,有雞蛋,有敖丙公仔玩具等等一些雜物。

現在我們想要最后得到的是米豆,雞蛋和敖丙玩具都被過濾掉。于是我們就可以搞兩個濾網,把敖丙玩具和雞蛋給過濾掉。

以最快的方式,我們可以寫if來把這個需求給搞掂,下面上代碼。

一個請求,我們使用Request對象來表示:

public class Request {
    // 請求的數據
    private String data;

    public String getData() {
        return data;
    }
    public void setData(String data) {
        this.data = data;
    }
}

針對請求,我們肯定是有一個接口處理請求的啦,我們使用Handler來表示:

public class Handler {

    public void handlerRequest(Request request) {

        // 得到請求的數據
        String data = request.getData();

        if (data.contains("雞蛋")) {
            filterEgg(data);
        }
        if (data.contains("敖丙工具")) {
            filterAoBing(data);
        }
        // 我到這里就能拿到米豆了。
    }

    private void filterAoBing(String data) {
        //doSomething
    }

    private void filterEgg(String data) {
        //doSomething
    }
}

上面的代碼大家不知道熟不熟悉,反正我就很熟悉,很多時候我就是這樣寫代碼的(在現實里邊很多代碼就是這樣的)。

1.2 如何更加優雅干掉敖丙和雞蛋?

在某年某月產品過來告訴我,需要新增一種類型想要過濾的「白菜」

在某年某月產品過來告訴我,需要新增一種類型想要過濾的「雞腿」

在某年某月產品過來告訴我,需要新增一種類型想要過濾的「雞頭」

于是我們的Handler處理就可能「膨脹」起來了,可能是這樣?

public class Handler {

    public void handlerRequest(Request request) {

        // 得到請求的數據
        String data = request.getData();

        if (data.contains("雞蛋")) {
            filterEgg(data);
        }
        if (data.contains("敖丙工具")) {
            filterAoBing(data);
        }
        if (data.contains("白菜")) {
            filterBaiCai(data);
        }
        if (data.contains("雞頭")) {
            filterJiTou(data);
        }
        if (data.contains("雞腿")) {
            filterJiTui(data);
        }
        // 我到這里就能拿到米豆了。
    }

    private void filterJiTou(String data) {
        //doSomething
    }

    private void filterJiTui(String data) {
        //doSomething
    }

    private void filterAoBing(String data) {
        //doSomething
    }

    private void filterEgg(String data) {
        //doSomething
    }
}

明顯的是,如果處理的流程改動比較大的話(需要增刪改其中的某個流程),那我每次都需要更改handlerRequest的代碼,增加/修改/刪除一個if和一個處理方法。

更加面向對象的方式是這樣的:將每個處理的方式抽象成一個類,每個類各司其職

無論是過濾敖丙還是過濾雞蛋還是過濾米豆,做的事都是過濾。我們就可以將其抽象成接口。于是我們就有一個接口,多個實現類

public interface Filter {
    // 過濾
    void doFilter(String data);
}

class FilterEgg implements Filter {

    @Override
    public void doFilter(String data) {
        //doSomething
    }
}

class FilterAoBing implements Filter {

    @Override
    public void doFilter(String data) {
        //doSomething
    }
}

class FilterBaiCai implements Filter {

    @Override
    public void doFilter(String data) {
        //doSomething
    }
}

class FilterJiTou implements Filter {

    @Override
    public void doFilter(String data) {
        //doSomething
    }
}

每個各司其職的Filter都有可能被執行,我們可以將其串成一條鏈,抽象一層對外只暴露一個方法來替代if。于是我們可以寫出一個FilterChain

public class FilterChain {
    List<Filter> filters = new ArrayList<>();

    public FilterChain() {
        filters.add(new FilterEgg());
        filters.add(new FilterAoBing());
        filters.add(new FilterBaiCai());
        filters.add(new FilterJiTou());
    }
    public void processData(String data) {
        for (Filter filter : filters) {
            filter.doFilter(data);
        }
    }
}

改造過后,我們的Handler就長這個樣子了:

public class Handler {
    public void handlerRequest(Request request) {
        // 得到請求的數據
        String data = request.getData();
        FilterChain filterChain = new FilterChain();
        // 處理數據
        filterChain.processData(data);
    }
}

如果我告訴你,這種的處理方式就是責任鏈模式,你會怎么想?

二、為什么責任鏈模式?

再來回顧一下,我做了些什么:

  1. 將處理的各個流程抽象為各個類(本來Handler里邊有多個if方法)
  2. 將多個類用Chain鏈起來,暴露一個方法給Handler使用
  3. done

下面我畫了一張對比圖:

是不是很簡單?說到底還是抽象了一層(將每個處理抽象為一個類而已)。

那為什么要這樣干?如果我要增加一個處理流程,我是得新增一個處理類,然后在鏈上增加相對應的類。操作也的確如此。

這不麻煩嗎?要便捷的話,我還不如直接增加一個if,一個處理方法來得方便呢。

用責任鏈模式的好處就是分工明確,解耦,容易維護

  • 將多個條件判定分散到各個的處理類上,相對于if else耦合性相對較低。
  • 增加一個具體的Handler處理類,不會影響到BaseHandler的代碼

責任鏈模式的缺點:

  • 項目里邊會有多個具體Handler類(因為每種處理都抽象為一個類,所以會有多個類)
  • 不好調試,初看代碼時不好閱讀。(對外只是一個doChain方法,而里邊由多個處理類來組成,還得看相應的調用順序)

三、再來聊聊責任鏈模式

我們從上面也可以看到責任鏈模式主要有以下特點:

  • 一個Handler接口,多個Handler處理類
  • 多個Handler處理類串起來形成一條鏈

有這兩個特點我就稱這些代碼運用了責任鏈模式。在翻閱資料或者看書的時候,你可能會看到:“責任鏈和不純責任鏈”

  • 純:請求執行到某個具體的Handler,該Handler要么自行處理然后結束請求,要么不處理繼續往下給別的Handler執行。
  • 不純:請求執行到某個具體的Handler,該Handler自行處理了,繼續往下給別的Handler執行。

還有就是將各個具體的Handler串成一條鏈,這里邊的實現會有各式各樣的:

  • 在我例子里是直接new出一個ArrayList,然后在構造方法里邊代碼手動add到ArrayList的
  • 有可能會在代碼里邊每個具體Handler都會記錄自己下一個Handler是誰
  • 有可能將Handler的初始化放在XML上
  • ….//反正各種操作最終還是會將各個Handler串起來

其實不必要在意純和不純的責任鏈模式,我們學設計模式是為了學它的思想

四、看看JavaWeb的Filter

在文章最開頭我就說了我們以前學過的Filter,其實Filter就是用了責任鏈模式。我們來簡單看看代碼:

我們在使用Filter過濾器的時候,要么在XML上配置<filter>,要么在代碼上寫上注解@WebFilter(filterName = "",urlPatterns = "")

這些配置都會在Web容器啟動的時候被讀取,讀完這些配置,會將你寫的Filter過濾器加到FilterChain里邊:

我們可以看到Filter接口下有很多都實現了doFilter

JavaWeb的Filter實際用到的也是責任鏈模式。

最后

設計模式本身不是一件很復雜的東西,像門面模式,模板方法模式都非常容易理解。學完了會有一種感覺:“啊?就這?

重要的是學完能不能用到實際的工作中,這是非常難能可貴的。我們寫代碼按照自身的思維寫if else是非常簡單的,而設計模式往往需要繞一個圈才能把功能實現。

但是,合理運用設計模式的代碼是非常好維護的。如果你懂設計模式,那代碼會看起來非常清晰。如果你不懂設計模式,你就會感嘆“這代碼是真的騷阿”(這就是我…)。

好好學習,希望有朝一日,別人看到我的代碼,在背后說「這人寫的代碼是真的騷,牛逼阿」。

參考資料:

  • 《設計模式之禪》
  • https://www.cnblogs.com/tanshaoshenghao/p/10741160.html
  • 如果大家想要實時關注我更新的文章以及分享的干貨的話,可以關注我的公眾號Java3y
我還沒有學會寫個人說明!

《柳葉刀》最新文章:主要城市疫情潛在傳播預報,將滯后武漢1-2周爆發

上一篇

谷歌SRE與運維工作的思考

下一篇

你也可能喜歡

這就是『責任鏈模式』?

長按儲存圖像,分享給朋友

ITPUB 每周精要將以郵件的形式發放至您的郵箱


微信掃一掃

微信掃一掃
重庆快乐10分苹果版本 按键精灵挂机赚钱 黑龙江时时彩 新浪体育nba比分直播 医疗众筹平台怎么赚钱 led体育比分牌 凯美瑞开滴滴快车赚钱吗 维加斯哪能赚钱 山东群英会 几台空闲电脑可以做什么赚钱 天津11选5 做传媒什么最赚钱 即时指数即时比分 小鱼赚钱安卓任务多吗 深圳风采 梦幻诛仙网游能赚钱 麻将游戏大全麻将游戏