Android 開發中常用設計模式解讀之-責任鏈(攔截器)

模式定義與設計解讀

設計模式晦澀的定義總是難懂,但同時這些定義又有着獨特的涵義。本文想通過最直觀的例子,把這些晦澀的定義反應在代碼層面上。代碼是設計模式最直觀的表達,當你看不懂定義時,代碼會說話。希望這篇解讀可以幫助到你。預計閱讀10分鐘。

定義

責任鏈模式 :使多個對象有機會處理請求,從而避免了請求的發送者和接收者之間的耦合關係。將這些對象連成一條鏈,並沿着這條鏈傳遞該請求,直到有對象處理它爲止。

注意一下加粗的關鍵字,解讀會重新定義這些難懂的定義。

解讀

在生活經常存在一種場景,完成一件事情,需要串行執行幾個步驟。比如把大象裝冰箱,需要三個步驟。

  1. 把冰箱門打開

  2. 把大象裝冰箱

  3. 把冰箱門關上

現在把這件事情抽象成代碼,並與上文中的定義對應。

  • 請求指把大象裝冰箱這件事情,對應一個數據結構 RequestContext

/**
 * 請求上下文
 */public class RequestContext {

  public Elephant mElephant;//大象

  public Refrigerator mRefrigerator;//冰箱

  public RequestContext(Elephant elephant, Refrigerator refrigerator) {
    mElephant = elephant;
    mRefrigerator = refrigerator;
  }  public static class Elephant{
  }  public static class Refrigerator{
  }

}
  • 一條鏈比較好理解,但是比較難抽象。既然做這件事需要串行好幾個步驟,鏈條上自然也是圍繞請求展開,既需要有獲取請求的方法RequestContext getContext(),也需要有執行請求的方法procceed(RequestContext context)。這個方法有開始執行的意思,也有請求串行傳遞後繼續執行的意思。

proceed 英 [prə'siːd]
vi. 開始;繼續進行;發生;

/**
 * 請求鏈
 */public interface Chain {  /**
   * 獲取請求實體
   */
  RequestContext getContext();  /**
   * 開始或繼續執行請求。
   */
  void proceed(RequestContext context);

}
  • 對象指1.2.3操作大象和冰箱的三個步驟,可以把每次步抽象成一個處理請求的對象,萬物皆對象。現在我們已經把請求封裝在了鏈條裏,那對於每個操作步驟,都直接來處理鏈條上的請求即可。

/**
 * 處理者
 */public interface Handler {  /**
   * 處理鏈條上的請求
   */
  void handle(Chain chain);

}
  • 發送者和接收者其實是指操作步驟的動作。每個操作步驟都會有以下流程

  1. 先接收請求(接收)

  2. 處理請求 (處理)

  3. 通知Chain,繼續執行請求。(發送)

每個操作步驟只需要關注自己需要如何操作請求,而不需要關心操作完成後下個步驟是什麼。只需要通知Chain,我執行完畢了,可以繼續執行下一步了。從而避免了發送者和接收者之間的耦合關係。那麼如何解耦呢,自然是鏈條來解決。鏈條上持有一系列操作步驟,存儲成List<Handler>,並且記錄當前執行到第幾步int index,再結合之前定義的Chain接口,Chain實現類如下

/**
  * 鏈實現類
  */public class ProcessChain implements Chain {  public List<Handler> mProcessors;  public RequestContext mChainContext;  public int mIndex;  public ProcessChain(List<Handler> processors, int index, RequestContext chainContext) {
    mProcessors = processors;
    mIndex = index;
    mChainContext = chainContext;
  }  @Override
  public RequestContext getContext() {    return mChainContext;
  }  @Override
  public void proceed(RequestContext processContext) {    if (mProcessors.size() > mIndex) {      //獲取當前處理者
      Handler processor = mProcessors.get(mIndex);      //更新index 與 Context
      ProcessChain nextChain = new ProcessChain(mProcessors, mIndex + 1, processContext);      //處理者執行處理步驟
      processor.handle(nextChain);
    }
  }
}

proceed方法可能會覺得有些繞,其實道理很簡單。着重理解一下nextChain即可,這裏是重新構造了一個Chain,其實也可以理解爲更新了一下Chain的數據,將index+1,以及更新RequestContext 。接下來講到Processor的實現類,二者結合起來理解會更容易nextChain

  • Handler實現類。接收的概念好理解,就是接口方法handle(Chain chain)中的參數。那麼什麼是發送呢,我們回到上文nextChain,實際handle(Chain chain)方法接收的參數就是下個節點的Chain,我們只需要在handle()方法中處理完畢後,繼續調用chain.proceed()方法,通知鏈條繼續執行就可以了。請求就這樣被髮送出去了。

/**
 * 打開冰箱
 */public class HandlerOpenRefrigerator implements Handler {  @Override
  public void handle(Chain chain) {
    RequestContext requestContext = chain.getContext();    //處理具體操作
    if(!requestContext.mRefrigerator.isOpen()) {
      requestContext.mRefrigerator.open();
    }    //處理完畢 繼續執行下個操作
    chain.proceed(requestContext);
  }
}
  • 攔截器 當我們執行到第二步時,需要裝大象,如果這時候門沒打開怎麼辦?任務執行不下去,我們可以選擇中斷鏈條,退出本次串行任務。這種鏈條上的任務傳遞和適當時機終止結合起來就是我們經常說的攔截器攔截器就是基於責任鏈模式,每個節點有自己的職責,同時可以選擇是否把任務傳遞給下一個環節

/**
 * 移動大象
 */public class MoveElephantHandler implements Handler {  @Override
  public void handle(Chain chain){
    RequestContext requestContext = chain.getContext();    //處理具體操作
    if(requestContext.mRefrigerator.isOpen()) {
      requestContext.mElephant.move();      //處理完畢 繼續執行下個操作
      chain.proceed(requestContext);
    }else{      //發生異常 中斷鏈條
      chain.abort();
    }
  }
}

推薦閱讀:阿里騰訊Android開發十年,到中年危機就只剩下這套移動架構體系了!

講到這裏再回頭讀一下最開始定義的那句話。你會有不一樣的理解。如果沒有,自己敲一遍代碼再試試。

  • 運行 依次構造請求,責任鏈,然後開始任務。

public class MyClass {
  public static void main(String[] args) {    //構造請求
    RequestContext requestContext = new RequestContext(new RequestContext.Elephant(),new RequestContext.Refrigerator());    //構造處理步驟
    List<Handler> list = new ArrayList<>();    //你也可以不加打開冰箱試試結果
    list.add(new OpenRefrigeratorHandler());    list.add(new MoveElephantHandler());    list.add(new CloseRefrigeratorHandler());    //執行任務
    ProcessChain processChain = new ProcessChain(list,0,requestContext);
    processChain.proceed(requestContext);
  }
}

Demo地址

思想理解與實踐運用

理解

責任鏈重點是在解耦,如何將一個任務的不同步驟之間沒有耦合關係,每個步驟專注負責自身,而不需要關心其他步驟,以及其他步驟的變化,這些變化可以交給更高層次來控制,而不是在任務節點上控制。比如上述例子中,如果裝大象上線後發現很容易失敗,需要先把大象切開(一個殘忍的舉例),那麼只需要在控制層添加list.add(new CutElephantHandler)即可。這種修改對於既有的代碼侵略性極低,因爲步驟之間解耦很徹底,並且這種可插拔式的代碼,組合性極強。使得代碼的擴展性和可維護性極高。

運用

  • 框架中的運用 :OkHttp中有使用Interceptor就是一個經典的例子。來看看源碼中的定義

/**
 * Observes, modifies, and potentially short-circuits requests going out and the corresponding
 * responses coming back in. Typically interceptors add, remove, or transform headers on the request
 * or response.
 */public interface Interceptor {  Response intercept(Chain chain) throws IOException;  interface Chain {    Request request();    Response proceed(Request request) throws IOException;

  }
}

是不是很熟悉。Okhttp中攔截器就是責任鏈模式運用。我們在進行應用開發的時候都會在請求中增加一些通用信息,比如在 header 中增加用戶的token信息等等以及在請求的過程中修改請求的 request 和 response。那麼我們就可以定義不同的Interceptor來處理。這些攔截器之間沒有任何關聯,只關注自己處理過程。現在回頭再去看看一些OKhttp的源碼解讀應該就很容易理解其精髓了。

  • 實際開發中的運用:除了在一些既有的框架中使用,我們在Andorid實際開發場景中如何運用呢。再舉個在項目中使用過的場景。我們的App,在HomeActivity啓動後,會依次做幾件事情

  1. 檢測升級,出現彈窗1

  2. 彈窗1關閉後,出現引導Window

  3. 引導圖關閉後,出現選擇語言彈窗。

如果需要是一步一步加上來的。比如第一個版本,我們只有檢測升級。代碼一定很直接,onCreate()方法裏showUpgradeDialog()。當來了第二個需求,代碼直接寫就會變成這樣。在升級彈窗的Dialog onDismissListenershowGuideWindow(),如果第一步未檢測到升級,還需要判斷if(!needUpgrade)直接showGuideWindow()。代碼寫到這裏,這兩個步驟已經耦合在一起了。引導window是否出現,跟檢測彈窗幾乎綁定在一起。如果有一天產品腦洞大開,我們先出引導再升級吧!想想你的代碼會怎麼改。
window的 onDismissListener 裏showUpgradeDialog()?如果使用責任鏈模式呢?在控制層換一下順序就好了。

  • 大家可以嘗試找一下項目裏類似的需求,一些任務需要串行執行的地方,如果耦合嚴重,試着重構一下。

總結

設計模式在開發實踐中,對代碼的可讀,可擴展,可維護性起到重要作用。在面試中,經常會和麪試者聊聊設計模式,大部分面試者都停留在設計概念本身,如果能闡述更多對設計模式的理解與在實踐中的運用,相信會對你的面試有幫助。本文也給大家舉一個設計模式學習的例子,從模式學習,思想理解到實踐運用。回頭再讀一下本文重點:

  • 責任鏈模式 :使多個對象有機會處理請求,從而避免了請求的發送者和接收者之間的耦合關係。將這些對象連成一條鏈,並沿着這條鏈傳遞該請求,直到有對象處理它爲止。

webp

Android設計模式等

關於Android進階的全部學習內容,我們這邊都有系統的知識體系以及進階視頻資料,有需要的朋友可以加羣免費領取安卓進階視頻教程,源碼,面試資料,羣內有大牛一起交流討論技術;818520403(包括自定義控件、NDK、架構設計、混合式開發工程師(React native,Weex)、性能優化、完整商業項目開發等)

webp

Android高級進階視頻



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