如何優雅的在業務中使用設計模式(程式碼如詩)

小呆呆666發表於2021-08-24

前言

有段時間沒寫文章了,最近沉迷Rust,無法自拔,鏽兒有毒;這真是門非常有趣的語言,很多地方的設計,真的是滿足了我所有的嚮往。

當然,這也不是一門簡單的語言,提出所有權的概念,引入了極多符號:mut、&mut、ref mut、&、*、as_mut、as_ref。。。讓人頭禿。。。

之前看到過一句話,覺得很不錯:學習Rust並不會給你帶來智商上的優越感,但或許會讓你重新愛上程式設計。

大家如果閱讀過一些開源框架的原始碼,可能會發現其中數不盡的抽象類,設計模式拈手而來,在功能框架中,可以使用設計模式隨心所欲的解耦;在實際的複雜業務中,當然也可以應用合適的設計模式。

這篇文章,我會將實際業務較為常見場景,如何合適將設計模式應用其中

  • 此處的應用絕不是生搬硬套,是我經過深思熟慮,並將較為複雜的業務進行全面重構後,得出的一套行之有效的思路歷程
  • 任何一個設計模式都是一個偉大的經驗及其思想總結,千人千面,如果對文章中內容,有不同的意見,希望你能在評論中提出,我們共同探討,共同進步

本文章是一篇弱程式碼型別文章,我會畫大量的圖片向大家展示,引用設計模式後,會對原有的業務流程,產生什麼樣的影響。

前置知識

這裡,需要了解下基礎知識,什麼是責任鏈模式和策略模式

責任鏈模式,在很多開源框架中都是有所應用,你如果聽到啥啥攔截器,基本就是責任鏈模式,責任鏈模式的思想很簡單,但是有很多種實現方式

  • 最簡單的連結串列實現就和OkHttp的攔截器實現大相徑庭
  • OkHttp的攔截器實現和Dio攔截器實現結構相同,但遍歷方式不一樣
  • 很多騷操作:我喜歡OkHttp的實現方式,喜歡dio的Api設計,結尾會給出一個結合這倆者思想的通用攔截器

策略模式,或是天生適合業務,同一模組不同型別業務,如果行為相同,或許就可以考慮使用策略模式去解耦了

責任鏈模式

這邊用Dart寫一個簡單的攔截器,dart和java非常像

  • 為了減少語言差異,我就不使用箭頭語法了
  • 下劃線表示私有

用啥語言不重要,這邊只是用程式碼簡單演示下思想

此處實現就用連結串列了;如果,使用陣列的形式,需要多寫很多邏輯,陣列的優化寫法在結尾給出,此處暫且不表

結構

  • 責任鏈的結構,通常有倆種結構
    • 連結串列結構:連結串列構建責任鏈,十分便捷的就能和下一節點建立聯絡
    • 陣列結構:陣列,用通用的List即可,方便增刪,不固定長度(別費勁的用固定長度Array了,例如:int[]、String[])

責任鏈結構

  • 實現一個連結串列實體很簡單
abstract class InterceptChain<T> {
  InterceptChain? next;

  void intercept(T data) {
    next?.intercept(data);
  }
}
複製程式碼

實現

  • 攔截器實現
/// 該攔截器以最簡單的連結串列實現
abstract class InterceptChain<T> {
  InterceptChain? next;

  void intercept(T data) {
    next?.intercept(data);
  }
}

class InterceptChainHandler<T> {
  InterceptChain? _interceptFirst;

  void add(InterceptChain interceptChain) {
    if (_interceptFirst == null) {
      _interceptFirst = interceptChain;
      return;
    }

    var node = _interceptFirst!;
    while (true) {
      if (node.next == null) {
        node.next = interceptChain;
        break;
      }
      node = node.next!;
    }
  }

  void intercept(T data) {
    _interceptFirst?.intercept(data);
  }
}
複製程式碼
  • 使用
    • 調整add順序,就調整了對應邏輯的節點,在整個責任鏈中的順序
    • 去掉intercept重寫方法中的super.intercept(data),就能實現攔截後續節點邏輯
void main() {
  var intercepts = InterceptChainHandler<String>();
  intercepts.add(OneIntercept());
  intercepts.add(TwoIntercept());
  intercepts.intercept("測試攔截器");
}

class OneIntercept extends InterceptChain<String> {
  @override
  void intercept(String data) {
    data = "$data:OneIntercept";
    print(data);
    super.intercept(data);
  }
}

class TwoIntercept extends InterceptChain<String> {
  @override
  void intercept(String data) {
    data = "$data:TwoIntercept";
    print(data);
    super.intercept(data);
  }
}
複製程式碼
  • 列印結果
測試攔截器:OneIntercept
測試攔截器:OneIntercept:TwoIntercept
複製程式碼

策略模式

結構

  • 策略模式最重要的:應該就是對抽象類的設計,對行為的抽象

策略模式應用

實現

  • 定義抽象類,抽象行為
/// 結合介面卡模式的介面適配:抽象必須實現行為,和可選實現行為
abstract class BusinessAction {
  ///建立相應資源:該行為必須實現
  void create();

  ///可選實現
  void dealIO() {}

  ///可選實現
  void dealNet() {}

  ///可選實現
  void dealSystem() {}

  ///釋放資源:該行為必須實現
  void dispose();
}
複製程式碼
  • 實現策略類
//Net策略
class NetStrategy extends BusinessAction {
  @override
  void create() {
    print("建立Net資源");
  }

  @override
  void dealNet() {
    print("建立IO資源");
  }

  @override
  void dispose() {
    print("建立Net資源");
  }
}

///IO策略
class IOStrategy extends BusinessAction {
  @override
  void create() {
    print("建立IO資源");
  }

  @override
  void dealIO() {
    print("處理IO邏輯");
  }

  @override
  void dispose() {
    print("釋放IO資源");
  }
}
複製程式碼
  • 使用
void main() {
  var type = 1;
  BusinessAction strategy;

  //不同業務使用不同策略
  if (type == 0) {
    strategy = NetStrategy();
  } else {
    strategy = IOStrategy();
  }

  //開始建立資源
  strategy.create();
  //......... 省略N多邏輯(其中某些場景,會有用到Net業務,和上面type是關聯的)
  //IO業務:開始處理業務
  strategy.dealIO();
  //......... 省略N多邏輯
  //釋放資源
  strategy.dispose();
}
複製程式碼
  • 結果
建立IO資源
處理IO邏輯
釋放IO資源
複製程式碼

適合的業務場景

這邊舉一些適合上述設計模式的業務場景,這些場景是真實存在的!

這些真實的業務,使用設計模式解耦和純靠if else懟,完全是倆種體驗!

程式碼如詩,這並不是一句玩笑話。

連環彈窗業務

業務描述

連環彈窗奪命call來襲。。。

  • A彈窗彈出:有確定和取消按鈕
    • 確定按鈕:B彈窗彈出(有檢視詳情和取消按鈕)
      • 檢視詳情按鈕:C彈窗彈出(有同意和拒絕按鈕)
        • 同意按鈕:D彈窗彈出(有檢視和下一步按鈕)
          • 檢視按鈕:E彈窗彈出(只有下一步按鈕)
            • 下一步按鈕:F彈窗彈出(結束)
          • 下一步按鈕:F彈窗彈出(結束)
        • 拒絕按鈕:流程結束
      • 取消按鈕:流程結束
    • 取消按鈕:流程結束

好傢伙,套娃真是無所不在,真不是我們程式碼套娃,實在是業務套娃,手動滑稽.jpng

img

  • 圖示彈窗業務

連環彈窗業務1

直接開搞

看到這個業務,大家會去做呢?

  • 有人可能會想,這麼簡單的業務還需要想嗎?直接寫啊!
    • A:在確定回撥裡面,跳轉B彈窗
    • B:檢視詳情按鈕跳轉C彈窗
    • 。。。
    • 好一通套後,終於寫完了

產品來了,加需求

B和C彈窗之間要加個預覽G彈窗,點選B的檢視詳情按鈕,跳轉預覽G彈窗,預覽G彈窗只有一個確定按鈕,跳轉C彈窗

img

  • 你心裡可能要想了,這特麼不是坑爹?
    • 業務本來就超吉爾套,我B彈窗裡面寫的跳轉程式碼要改,傳參要改,而且還要加彈窗!
  • 先要找產品撕逼,撕完畢後
    • 然後繼續在屎山上,小心翼翼的再拉了坨shit
    • 這座克蘇魯山初成規模

連環彈窗業務2

產品又來了,第一稿需求不合理,需要調整需求

交換C和D彈窗位置,D彈窗點選下一步的時候,需要加一個校驗請求,通過後才能跳轉到C彈窗

img

  • 你眉頭一皺,發現事情沒有表面這麼簡單
    • 由於初期圖簡單,幾乎都寫在一個檔案裡,眼花繚亂彈窗回撥太多,而且彈窗樣式也不一樣
    • 現在改整個流程,導致你整個人腦子嗡嗡響
  • 心中怒氣翻湧,找到產品說

img

  • 回來,坐在椅子上,心裡想:
    • 老夫寫的程式碼天衣無縫,這什麼幾把需求
    • 可惡,這次測試,起碼要給我多提十幾個BUG

image-20210822215435299

  • 克蘇魯山開始猙獰

連環彈窗業務3

產品飄來,加改需求:如此,如此,,,這般,這般,,,

  • 你....

img

產品:改下,,,然後,扔給你幾十頁的PRD

  • 你看了看這改了幾十版的克蘇魯山,這幾十個彈窗邏輯居然都寫在一個檔案裡,快一萬行的程式碼。。。
    • 心裡不禁想:本帥比寫的程式碼果然牛批,或許這就是藝術!藝術總是曲高和寡,難被人理解!而我的程式碼更牛批,連我自己都看不懂了!

16c3-ikhvemy5945899

  • 心想:事了拂衣去,深藏功與名

img

重構

隨著業務的逐漸複雜,最初的設計缺點會逐漸暴露;重構有缺陷的程式碼流程,變得勢在必行,這會極大降低維護成本

如果心中對責任鏈模式有一些概念的話,會發現上面的業務,極其適合責任鏈模式!

對上面的業務進行分析,可以明確一些事

  • 這個業務是一個鏈式的,有著較明確方向性,單向,從頭到尾指向
  • 業務拆分開,可以將一個彈窗作為顆粒度,一個彈窗作為節點
  • 上級的業務節點可以對下級節點攔截(點選取消,拒絕按鈕,不再進行後續業務)

重構上面的程式碼,只要明確思想和流程就行了

第一稿業務

  • 業務流程

連環彈窗業務1

  • 責任鏈

責任鏈業務1

  • 程式碼:簡寫
void main() {
  var intercepts = InterceptChainHandler<String>();
  intercepts.add(AIntercept());
  intercepts.add(BIntercept());
  intercepts.add(CIntercept());
  intercepts.add(DIntercept());
  intercepts.add(EIntercept());
  intercepts.add(FIntercept());
  intercepts.intercept("測試攔截器");
}
複製程式碼

第二稿業務

  • 業務流程

連環彈窗業務2

  • 責任鏈

責任鏈業務2

  • 程式碼:簡寫
void main() {
  var intercepts = InterceptChainHandler<String>();
  intercepts.add(AIntercept());
  intercepts.add(BIntercept());
  intercepts.add(GIntercept());
  intercepts.add(CIntercept());
  intercepts.add(DIntercept());
  intercepts.add(EIntercept());
  intercepts.add(FIntercept());
  intercepts.intercept("測試攔截器");
}
複製程式碼

第三稿業務

  • 業務流程

連環彈窗業務3

  • 責任鏈

責任鏈業務3

  • 程式碼:簡寫
void main() {
  var intercepts = InterceptChainHandler<String>();
  intercepts.add(AIntercept());
  intercepts.add(BIntercept());
  intercepts.add(GIntercept());
  intercepts.add(DIntercept());
  intercepts.add(CIntercept());
  intercepts.add(EIntercept());
  intercepts.add(FIntercept());
  intercepts.intercept("測試攔截器");
}
複製程式碼

總結

經過責任鏈模式重構後,業務節點被明確的區分開,整個流程從程式碼上看,都相當的清楚,維護將變的異常輕鬆;或許,此時能感受到一些,程式設計的樂趣了

img

花樣彈窗業務

業務描述

這邊來描述一個業務,這個業務場景真實存在某辦公軟體

  • 進入APP首頁,然後和後臺建立一個長連線
  • 後臺某些工單處理後,會通知APP處理,此時app會彈出處理工單的彈窗(app頂部)
  • 彈窗型別很多:工單處理彈窗,流程審批彈窗,邀請型別彈窗,檢視工單詳情彈窗,提交資訊彈窗。。。
  • 彈窗彈出型別,是根據後臺給的Type進行判斷:從而彈出不同型別彈窗、點選其按鈕,跳轉不同業務,傳遞不同引數。

花樣彈窗業務

分析

確定設計

這個業務,是一種漸變性的引導你搭建克蘇魯程式碼山

  • 在前期開發的時候,一般只有倆三種型別彈窗,前期十分好做;根本不用考慮如何設計,抬手一行程式碼,反手一行程式碼,就能搞定
  • 但是後來整個業務會漸漸的鬼畜,不同型別會慢慢加到幾十種之多!!!

首先這個業務,使用責任鏈模式,肯定是不合適的,因為彈窗之間的耦合性很低,並沒有什麼明確的上下游關係

但是,這個業務使用策略模式非常的適合!

  • type型別明確:不同型別彈出不同彈窗,按鈕執行不同邏輯
  • 抽象行為明確:一個按鈕就是一種行為,不同行為的實現邏輯大相徑庭

抽象行為

多樣彈窗的行為抽象,對應其按鈕就行了

確定、取消、同意、拒絕、檢視詳情、我知道了、提交

直接畫圖來表示吧

花樣彈窗業務-抽象行為

實現

來看下簡要的程式碼實現,程式碼不重要,重要的是思想,這邊簡要的看下程式碼實現流程

  • 抽象基類
/// 預設實現拋異常,可提醒未實現方法被誤用
abstract class DialogAction {
  ///確定取消
  void onConfirm() {
    throw 'DialogAction:not implement onConfirm()';
  }

  ///取消
  void onCancel() {
    throw 'DialogAction:not implement onCancel()';
  }

  ///同意
  void onAgree() {
    throw 'DialogAction:not implement onAgree()';
  }

  ///拒絕
  void onRefuse() {
    throw 'DialogAction:not implement onRefuse()';
  }

  ///檢視詳情
  void onDetail() {
    throw 'DialogAction:not implement onDetail()';
  }

  ///我知道了
  void onKnow() {
    throw 'DialogAction:not implement onKnow()';
  }

  ///提交
  void onSubmit() {
    throw 'DialogAction:not implement onSubmit()';
  }
}
複製程式碼
  • 實現邏輯類
class OneStrategy extends DialogAction {
  @override
  void onConfirm() {
    print("確定");
  }

  @override
  void onCancel() {
    print("取消");
  }
}

class TwoStrategy extends DialogAction{
  @override
  void onAgree() {
    print("同意");
  }
  
  @override
  void onRefuse() {
    print("拒絕");
  }
}

//........省略其他實現
複製程式碼
  • 使用
void main() {
  //根據介面獲取
  var type = 1;
  DialogAction strategy;
  switch (type) {
    case 0:
      strategy = DefaultStrategy();
      break;
    case 1:
      strategy = OneStrategy();
      break;
    case 2:
      strategy = TwoStrategy();
      break;
    case 3:
      strategy = ThreeStrategy();
      break;
    case 4:
      strategy = FourStrategy();
      break;
    case 5:
      strategy = FiveStrategy();
      break;
    default:
      strategy = DefaultStrategy();
      break;
  }

  //聚合彈窗按鈕觸發事件
  BusinessDialog(
    //確定按鈕
    onConfirm: () {
      strategy.onConfirm();
    },
    //取消按鈕
    onCancel: () {
      strategy.onCancel();
    },
    //同意按鈕
    onAgree: () {
      strategy.onAgree();
    },
    //拒絕按鈕
    onRefuse: () {
      strategy.onRefuse();
    },
    //檢視詳情按鈕
    onDetail: () {
      strategy.onDetail();
    },
    //我知道了按鈕
    onKnow: () {
      strategy.onKnow();
    },
    //提交按鈕
    onSubmit: () {
      strategy.onSubmit();
    },
  );
}
複製程式碼
  • 圖示

花樣彈窗業務-業務流程

一個複雜業務場景的演變

我們看下,一個簡單的提交業務流,怎麼逐漸變的猙獰

我會逐漸給出一個合適的解決方案,如果大家有更好的想法,務必在評論區告訴鄙人

業務描述:我們的車子因不可抗原因壞了,要去維修廠修車,工作人員開始登記這個損壞車輛。。。

業務的演變

第一稿

初始業務

登記一個維修車輛的流程,實際上還是滿麻煩的

  • 登記一個新車,需要將車輛詳細資訊登記清楚:車牌、車架、車型號、車輛型別、進出場時間、油量、里程。。。
  • 還需要登記一下使用者資訊:姓名、手機號、是否隸屬公司。。。
  • 登記車損程度:車頂、車底、方向盤、玻璃、離合器、剎車。。。
  • 車內物品:車座皮套、工具。。。
  • 以及其他我沒想到。。。
  • 最後:提交所有登記好的資訊

第一稿,業務流程十分清晰,細節複雜,但是做起來不難

車輛登記-第一稿

第二稿(實際是多稿聚合):增加下述幾個流程

外部登記:外部登記了一個維修車輛部分資訊(後臺,微信小程式,H5等等),需要在app上完善資訊,提交介面不同(必帶車牌號)

快捷洗車:洗車業務極其常見,快捷生成對應資訊,提交介面不同

預約訂單登記:預約好了車輛一部分一些資訊,可快捷登記,提交介面不同(必帶車牌號)

因為登記維修車輛流程,登記車輛資訊流程極其細緻繁瑣,我們決定複用登記新車模組

  • 因為此處邏輯大多涉及開頭和結尾,中間登記車輛資訊操作幾乎未改動,複用想法是可行的
  • 如果增加車輛登記項,新的三個流程必須也需要提交這些資訊,複用勢在必行

因為這一稿需求,業務也變得愈加複雜

車輛登記-第二稿

第三稿

現在要針對不同的車輛型別,做不同的處理;車型別分:個人車,集團車

不同型別的登記,在提交的時候,需要校驗不同的資訊;校驗不通過,需要提示使用者,並且不能進行提交流程

提交完,處理下通用也通用業務,然後跳轉的某個頁面

第三稿的描述不多,但是,也同樣大大的增加了複雜度

  • 尤其是不同型別校驗過程還不同,還能中斷後續提交流程
  • 提交流程後,還需要跳轉通用頁面

車輛登記-第三稿

開發探討

第一稿

  • 業務流程

車輛登記-第一稿

  • 開發

正常流程開發、、、

第二稿

  • 業務流程

車輛登記-第二稿

  • 思考

對於第二稿的業務,可以好好的思考,可以怎麼去開發?

開頭和結尾需要單獨寫判斷,去處理不同流程的業務,這至少要寫倆個大的判斷模組,接受資料的入口模組可能還要寫判斷

這樣就非常適合策略模式去做了

開頭根據執行的流程,選擇相應的策略物件,後續將邏輯塊替換抽象的策略方法就OK了,大致流程如下

車輛登記-第二稿(策略模式)

第三稿

業務流程

車輛登記-第三稿

探討

  • 第三稿的需求,實際上,已經比較複雜了

    • 整個流程中摻雜著不同業務流程處理,不同流程邏輯又擁有阻斷下游機制(綠色模組)
    • 下游邏輯又會合流(結尾)的多種變換
  • 在這一稿的需求

    • 使用策略模式肯定是可以的
    • 阻斷那塊(綠色模組)需要單獨處理下:抽象方法應該擁有返回值,外層根據返回值,判斷是否進行後續流程
    • 但!這!也太不優雅了!
  • 思考上面業務一些特性

    • 攔截下游機制
    • 上游到下游、方向明確
    • 隨時可能插入新的業務流程。。。

可以用責任鏈模式!需要做一些小改動!這地方,我們需要將頻繁變動的模組用責任鏈模式全都隔離出來

  • 看下,使用責任鏈模式改造後流程圖

車輛登記-第三稿(責任鏈模式)

瀏覽上述流程圖可發現,本來是極度雜亂糅合的業務,可以被設計相對更加平行的結構

  • 對於上述流程,可以進一步分析,並進一步簡化:對整體業務分析,我們需要去關注其變或不變的部分

    • 不變:整體業務變動很小的是,登記資訊流程(主題邏輯這塊),此處的相關變動是很小的,對所有流程也是共用的部分
    • 變:可以發現,開頭和結尾是變動更加頻繁的部分,我們可以對此處邏輯進行整體的抽象
  • 抽象多變的開頭和結尾

車輛登記-第三稿(責任鏈模式——簡化)

  • 所以我們抽象攔截類,可以做一些調整
abstract class InterceptChainTwice<T> {
  InterceptChainTwice? next;

  void onInit(T data) {
    next?.onInit(data);
  }

  void onSubmit(T data) {
    next?.onSubmit(data);
  }
}
複製程式碼

來看下簡要的程式碼實現,程式碼不重要,主要看看實現流程和思想

  • 抽象攔截器
abstract class InterceptChainTwice<T> {
  InterceptChainTwice? next;

  void onInit(T data) {
    next?.onInit(data);
  }

  void onSubmit(T data) {
    next?.onSubmit(data);
  }
}

class InterceptChainTwiceHandler<T> {
  InterceptChainTwice? _interceptFirst;

  void add(InterceptChainTwice interceptChain) {
    if (_interceptFirst == null) {
      _interceptFirst = interceptChain;
      return;
    }

    var node = _interceptFirst!;
    while (true) {
      if (node.next == null) {
        node.next = interceptChain;
        break;
      }
      node = node.next!;
    }
  }

  void onInit(T data) {
    _interceptFirst?.onInit(data);
  }

  void onSubmit(T data) {
    _interceptFirst?.onSubmit(data);
  }
}
複製程式碼
  • 實現攔截器
/// 開頭通用攔截器
class CommonIntercept extends InterceptChainTwice<String> {
  @override
  void onInit(String data) {
    //如果有車牌,請求介面,獲取資料
    //.................
    //填充頁面
    super.onInit(data);
  }
}

/// 登記新車攔截器
class RegisterNewIntercept extends InterceptChainTwice<String> {
  @override
  void onInit(String data) {
    //處理開頭針對登記新車的單獨邏輯
    super.onInit(data);
  }

  @override
  void onSubmit(String data) {
    var isPass = false;
    //如果校驗不過,攔截下游邏輯
    if (!isPass) {
      return;
    }
    // ......
    super.onSubmit(data);
  }
}

/// 省略其他實現
複製程式碼
  • 使用
void main() {
  var type = 0;
  var intercepts = InterceptChainTwiceHandler();

  intercepts.add(CommonIntercept());
  intercepts.add(CarTypeDealIntercept());
  if (type == 0) {
    //登記新車
    intercepts.add(CommonIntercept());
  } else if (type == 1) {
    //外部登記
    intercepts.add(OutRegisterIntercept());
  } else if (type == 2) {
    //快捷洗車
    intercepts.add(FastWashIntercept());
  } else {
    //預約訂單登記
    intercepts.add(OrderRegisterIntercept());
  }
  intercepts.add(TailIntercept());

  //業務開始
  intercepts.onInit("傳入資料來源");

  //開始處理N多邏輯
  //............................................................
  //經歷了N多邏輯

  //提交按鈕觸發事件
  SubmitBtn(
    //提交按鈕
    onSubmit: () {
      intercepts.onSubmit("傳入提交資料");
    },
  );
}
複製程式碼

總結

關於程式碼部分,關鍵的程式碼,我都寫出來,用心看看,肯定能明白我寫的意思

也不用找我要完整程式碼了,這些業務demo程式碼寫完後,就刪了

本欄目這個業務,實際上是非常常見的的一個業務,一個提交流程與很多其它的流程耦合,整個業務就會慢慢的變的鬼畜,充滿各種判斷,很容易讓人陷入泥濘,或許,此時可以對已有業務進行思考,如何進行合理的優化

該業務的演變歷程,和開發改造是本人的一次思路歷程,如大家有更好的思路,還請不吝賜教。

通用攔截器

我結合OkHttp的思想和Dio的API,封裝了倆個通用攔截器,這邊貼下程式碼,如果哪裡有什麼不足,請及時告知本人

說明下:這是Dart版本的

抽象單方法

///一層通用攔截器,T的型別必須一致
abstract class InterceptSingle<T> {
  void intercept(T data, SingleHandler handler) => handler.next(data);
}

///新增攔截器,觸發攔截器方法入口
class InterceptSingleHandler<T> {
  _InterceptSingleHandler _handler = _InterceptSingleHandler(
    index: 0,
    intercepts: [],
  );

  void add(InterceptSingle intercept) {
    //一種型別的攔截器只能新增一次
    for (var item in _handler.intercepts) {
      if (item.runtimeType == intercept.runtimeType) {
        return;
      }
    }

    _handler.intercepts.add(intercept);
  }

  void delete(InterceptSingle intercept) {
    _handler.intercepts.remove(intercept);
  }

  void intercept(T data) {
    _handler.next(data);
  }
}

///------------實現不同處理器 參照 dio api設計 和 OkHttp實現思想---------------
abstract class SingleHandler {
  next(dynamic data);
}

///實現init處理器
class _InterceptSingleHandler extends SingleHandler {
  List<InterceptSingle> intercepts;

  int index;

  _InterceptSingleHandler({
    required this.index,
    required this.intercepts,
  });

  @override
  next(dynamic data) {
    if (index >= intercepts.length) {
      return;
    }

    var intercept = intercepts[index];
    var handler =
        _InterceptSingleHandler(index: index + 1, intercepts: intercepts);

    intercept.intercept(data, handler);
  }
}
複製程式碼

抽象雙方法

///倆層通用攔截器,T的型別必須一致
abstract class InterceptTwice<T> {
  void onInit(T data, TwiceHandler handler) => handler.next(data);

  void onSubmit(T data, TwiceHandler handler) => handler.next(data);
}

///新增攔截器,觸發攔截器方法入口
class InterceptTwiceHandler<T> {
  _TwiceInitHandler _init = _TwiceInitHandler(index: 0, intercepts: []);
  _TwiceSubmitHandler _submit = _TwiceSubmitHandler(index: 0, intercepts: []);

  void add(InterceptTwice intercept) {
    //一種型別的攔截器只能新增一次
    for (var item in _init.intercepts) {
      if (item.runtimeType == intercept.runtimeType) {
        return;
      }
    }

    _init.intercepts.add(intercept);
    _submit.intercepts.add(intercept);
  }

  void delete(InterceptTwice intercept) {
    _init.intercepts.remove(intercept);
    _submit.intercepts.remove(intercept);
  }

  void onInit(T data) {
    _init.next(data);
  }

  void onSubmit(T data) {
    _submit.next(data);
  }
}

///------------實現不同處理器 參照 dio api設計 和 OkHttp實現思想---------------
abstract class TwiceHandler {
  next(dynamic data);
}

///實現init處理器
class _TwiceInitHandler extends TwiceHandler {
  List<InterceptTwice> intercepts;

  int index;

  _TwiceInitHandler({
    required this.index,
    required this.intercepts,
  });

  @override
  next(dynamic data) {
    if (index >= intercepts.length) {
      return;
    }

    var intercept = intercepts[index];
    var handler = _TwiceInitHandler(index: index + 1, intercepts: intercepts);

    intercept.onInit(data, handler);
  }
}

///實現submit處理器
class _TwiceSubmitHandler extends TwiceHandler {
  List<InterceptTwice> intercepts;

  int index;

  _TwiceSubmitHandler({
    required this.index,
    required this.intercepts,
  });

  @override
  next(dynamic data) {
    if (index >= intercepts.length) {
      return;
    }

    var intercept = intercepts[index];
    var handler = _TwiceSubmitHandler(index: index + 1, intercepts: intercepts);

    intercept.onSubmit(data, handler);
  }
}
複製程式碼

最後

第一次,寫這種結合業務的文章

如有收穫,還請點個贊,讓我感受到你讀有所獲~~~~

img

感謝閱讀,下次再會~~

img

相關文章