責任鏈模式及OkHttp中的實現

責任鏈模式及OkHttp中的實現

責任鏈模式

責任鏈模式是對一個事件的處理方法,所有能對事件進行處理的對象按順序形成一個鏈表.事件經過鏈表中每個處理對象輪流處理.如果有返回值.則返回也是順著這條鏈表反向返回.這個鏈表是先進后出模式.

  • 在現實中的責任鏈模型之一就是網絡連接.對與程序猿而言,七層或五層的網絡連接模型是肯定知道的.

當一個網絡請求發出時,需要經過應用層->傳輸層->網絡層->連接層->物理層

收到響應后正好反過來,物理層->連接層->網絡層->傳輸層->應用層

在請求經過各層時,由每層輪流處理.每層都可以對請求或響應進行處理.并可以中斷鏈接,以自身為終點返回響應

  • 另一個現實模型就是Web服務器的請求緩存

一個請示從客戶端發送到Web服務器需要經過客戶端->中間服務器->反向代理->Web端緩存(如Redis)->Web數據庫

除了Web數據庫是請求的點外,中間所有層都可以緩存數據.如果有緩存時會終止請求,以自身為終點返回數據.

如果途經的層沒有緩存,則會在收到下一級的返回的數據時對數據進行緩存.然后返回上一層.

在設計模式中,負責鏈模式就是對這種順序處理事件的行為的抽象,通過接口來定義處理事件的方法.順序分發/處理事件.

  • 每個責任人實現相同的接口,處理一個事件對象
  • 讓事件對象責任人之間順序傳遞
  • 事件的處理結果的返回是逆序的
  • 責任鏈中的每個責任人都可以有權不繼續傳遞事件,以自身為終點處理事件返回結果

OkHttp中的責任鏈模式

Okhttp中,Intercepter就是典型的責任鏈械的實現.它可以設置任意數量的Intercepter來對網絡請求及其響應做任何中間處理.比如設置緩存,Https的證書驗證,統一對請求加密/防串改,打印自定義Log,過濾請求等.

interface Intercepter{
Response response chain(Chain chain);
}

這個接口很簡單,拿到Chain對象.最后返回個Response,那這個對象是什么鬼??

它有兩個重要的方法
public Request getQuest()public Response process(Request quest)
拿到請求及設置請求拿到響應.
一般的實現是先拿到請求.然后對請求做一番蹂躪,然后process一下,拿到Response,折騰一下.然后return

Response response chain(Chain chain){
  Requset quest = chian.getRequset()
  ooxx(request);
  Response response = chian.process(quest);
  xxoo(response);
  return response;
}

或許它不知道,Resquse其實是被上家ooxx過的,Respnse也是被下家xxoo過的.

具體的實現可以去看一下源碼,我這里就寫一下自己理解的超簡單的實現

public class Okhttp {
private List<Intercepter> mIntercepters=new LinkedList<>();
private Request mRequest;
private int mIndex;
private Callback mCallback;
private Chain mChain=new Chain();
private Intercepter mNetIntercepter =new Intercepter() {
@Override
public Response chain (Chain chain) {
  //這里是真實的發送網絡請求
  return 網絡請示的響應;
  }
};

/**
* 添加攔截器,后加的放最前面
* @param intercper 攔截器
*/
public void addIntercepter(Intercepter intercper){
  mIntercepters.add(0, intercper);
}

/**
* OkHttp請求的入口
* @param request 請示
* @param callback 回調
*/
public void execute(Request request ,Callback callback){
  mCallback=callback;
  mIndex=0;
  mRequest=request;
  new Thread(new Runnable() {
  @Override
  public void run () {
    Response response=mChain.process(mRequest);
    mCallback.onResponse(response);
    }
  }).start();
}

/**
* 定義的一個類用與遞歸的方式完成責任鏈發送請求獲取響應
*/
private class Chain {
public Request getRequest(){
  return mRequest;
}

/**
* 順序獲取攔截器,傳遞chain對象給攔截器,獲取響應
* @return 請求的響應
*/
private Response getResponse () {
  Intercepter intercepter = mIntercepters.get(mIndex);
  mIndex++;
  return intercepter.chain(this);
}

/**
* 如果責任鏈沒走完,則順序從責任鏈中獲取攔截器,處理請求
* 否則由真正的負責網絡請求的攔截器處理請求
* @param request 請求
* @return 響應
*/
Response process(Request request){
  mRequest=request;
  if (mIndex>=mIntercepters.size()){
    return Okhttp.this.mNetIntercepter.chain(this);
  }else{
    return getResponse();
  }
}
}
}

不得不說程序猿寫文章真是容易騙字數.我已經極力在簡化了.代碼還是差不多上百行.

應該寫得夠簡單吧.重點也就幾個

  1. 內部有個處理真實網絡請求的Intercepter,做為最后的接盤俠.
  2. 返回響應的是intercepter.chain(this),如果這個攔截器調用了chainprocess()方法就形成遞歸,否則就是終斷了請求的傳遞
  3. 整個責任鏈的傳遞都是同步的.整體在子線程運行,最后通過回調返回響應.

還有一個很經典的實現就是Android的事件分發機制.這里我就不貼代碼騙字數了.網上隨便一找一堆.

責任鏈模式的用途

當業務邏輯需要形成一個事件處理流時,就可以考慮使用責任鏈模式.通過接口來規范中間環節的行為,專注與事件流的傳遞.可以隨意擴展及調整中間環節.

我在實際業務中的有一個場景中使用了責任鏈模式.(當時使用的時候其實并不清楚)

  1. 在一個列表中可以彈出一個篩選菜單,菜單項是不定的.某些項選擇后可以增加更多的選項條目
  2. 選項條目類型不同.有單選,多選,輸入等.
  3. 點擊完成時才把所有篩選條目形成對應的網絡請求參數

實際處理起來很簡易.定義了兩個類

interface ParamsSetAble{
void setParams(NetParams params);
}

public class NetParams{}

每個條目實現ParamsSetAble接口用與設置參數.

NetParams類用與收集參數,最后轉換成網絡請求所用的格式

當點擊完成時,遍歷所有的條目,傳遞NetParams對象.最后把這個對象傳遞給網絡請求的類.

在傳遞時進行可以進行參數檢查,錯誤時可以中斷傳遞,提示錯誤.我這里是通過手動拋異常來中斷的.在外部統一catch處理.如果用Rxjava可以不用catch,在onError里統一管理.如Observer.error(new 自定義異常(中斷提示信息))

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,837評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,196評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,688評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,654評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,456評論 6 406
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,955評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,044評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,195評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,725評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,608評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,802評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,318評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,048評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,422評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,673評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,424評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,762評論 2 372

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,781評論 18 139
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,605評論 25 708
  • 1 場景問題# 1.1 申請聚餐費用## 來考慮這樣一個功能:申請聚餐費用的管理。 很多公司都有這樣的福利,就是項...
    七寸知架構閱讀 3,162評論 3 58
  • 0.作業要求 使用ASN.1編寫一個數據結構。數據結構自己考慮。 分別使用asn1c、JavaAsn1Compil...
    htkz閱讀 16,103評論 5 7
  • 如果看過我前面幾篇關于Runtime的文章,應該知道Runtime的消息發送機制的原理是對象根據方法編號SEL去映...
    FITZ9311閱讀 449評論 0 3