現在的位置: 首頁 > 移動開發 > 正文

什麼是責任鏈模式?為什麼要責任鏈模式?

2020年02月21日 移動開發 ⁄ 共 4214字 ⁄ 字型大小 評論關閉

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

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

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

  一、什麼是責任鏈模式?

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

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

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

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

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

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

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

  以最快的方式,我們可以寫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 }}

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

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

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

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

  於是我們的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 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); }}

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

  二、為什麼責任鏈模式?

  說到底還是抽象了一層(將每個處理抽象為一個類而已)。那為什麼要這樣干?如果我要增加一個處理流程,我是得新增一個處理類,然後在鏈上增加相對應的類。操作也的確如此。

  這不麻煩嗎?要便捷的話,我還不如直接增加一個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上配置,要麼在代碼上寫上註解@WebFilter(filterName = "",urlPatterns = "")

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

  我們可以看到Filter介面下有很多都實現了doFilter:

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

  最後

  設計模式本身不是一件很複雜的東西,像門面模式,模板方法模式都非常容易理解。重要的是學完能不能用到實際的工作中,這是非常難能可貴的。我們寫代碼按照自身的思維寫if else是非常簡單的,而設計模式往往需要繞一個圈才能把功能實現。

抱歉!評論已關閉.