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


前言

有段時間沒寫文章了,最近沉迷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("處理Net邏輯");
  }

  @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彈窗彈出(結束)
        • 拒絕按鈕:流程結束
      • 取消按鈕:流程結束
    • 取消按鈕:流程結束

好家伙,套娃真是無所不在,真不是我們代碼套娃,實在是業務套娃,手動滑稽.png

img

  • 圖示彈窗業務

連環彈窗業務1

直接開搞

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

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

產品來了,加需求

B和C彈窗之間要加個預覽G彈窗,點擊B的查看詳情按鈕,跳轉預覽G彈窗;預覽G彈窗只有一個確定按鈕,點擊后跳轉C彈窗

img

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

連環彈窗業務2

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

交換C和D彈窗位置,邏輯調整:D彈窗點擊下一步的時候,需要加一個校驗請求,通過后跳轉到C彈窗,點擊查看按鈕跳轉彈窗F

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;
  }

  //聚合彈窗按鈕觸發事件(不同彈窗的確定按鈕,皆可聚合為一個onConfirm事件,其它同理)
  BusinessDialog(
    //通過傳入的type,顯示對應類型的彈窗
    type: type,
    //確定按鈕
    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(RegisterNewCarIntercept());
  } 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(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 {
  /// span: 設置該參數,可控跨越多級節點
  /// 默認0,則不跨越節點(遍歷所有節點)
  next(dynamic data, {int span = 0});
}

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

  int index;

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

  @override
  next(dynamic data, {int span = 0}) {
    if ((index + span) >= intercepts.length) return;

    var intercept = intercepts[index + span];
    var handler = _InterceptSingleHandler(
      index: index + (span + 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(intercepts: []);
  _TwiceSubmitHandler _submit = _TwiceSubmitHandler(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 {
  /// span: 設置該參數,可控跨越多級節點
  /// 默認0,則不跨越節點(遍歷所有節點)
  next(dynamic data, {int span = 0});
}

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

  int index;

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

  @override
  next(dynamic data, {int span = 0}) {
    if ((index + span) >= intercepts.length) return;

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

    intercept.onInit(data, handler);
  }
}

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

  int index;

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

  @override
  next(dynamic data, {int span = 0}) {
    if ((index + span) >= intercepts.length) {
      return;
    }

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

    intercept.onSubmit(data, handler);
  }
}

最后

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

如有收獲,還請點個贊,讓我感受一下,各位是否讀有所獲~~

img

感謝閱讀,下次再會~~

img


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM