前言
有段時間沒寫文章了,最近沉迷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彈窗彈出(結束)
- 檢視按鈕:E彈窗彈出(只有下一步按鈕)
- 拒絕按鈕:流程結束
- 同意按鈕:D彈窗彈出(有檢視和下一步按鈕)
- 取消按鈕:流程結束
- 檢視詳情按鈕:C彈窗彈出(有同意和拒絕按鈕)
- 取消按鈕:流程結束
- 確定按鈕:B彈窗彈出(有檢視詳情和取消按鈕)
好傢伙,套娃真是無所不在,真不是我們程式碼套娃,實在是業務套娃,手動滑稽.png
- 圖示彈窗業務
直接開搞
看到這個業務,大家會去怎麼做呢?
- 有人可能會想,這麼簡單的業務還需要想嗎?直接寫啊!
- A:在確定回撥裡面,跳轉B彈窗
- B:檢視詳情按鈕跳轉C彈窗
- 。。。
- 好一通套後,終於寫完了
產品來了,加需求
B和C彈窗之間要加個預覽G彈窗,點選B的檢視詳情按鈕,跳轉預覽G彈窗;預覽G彈窗只有一個確定按鈕,點選後跳轉C彈窗
- 你心裡可能要想了,這特麼不是坑爹?
- 業務本來就超吉爾套,我B彈窗裡面寫的跳轉程式碼要改,傳參要改,而且還要加彈窗!
- 先要去找產品撕比,撕完後
- 然後繼續在屎山上,小心翼翼的再拉了坨shit
- 這座克蘇魯山初成規模
產品又來了,第一稿需求不合理,需要調整需求
交換C和D彈窗位置,邏輯調整:D彈窗點選下一步的時候,需要加一個校驗請求,通過後跳轉到C彈窗,點選檢視按鈕跳轉彈窗F
- 你眉頭一皺,發現事情沒有表面這麼簡單
- 由於初期圖簡單,幾乎都寫在一個檔案裡,眼花繚亂彈窗回撥太多,而且彈窗樣式也不一樣
- 現在改整個流程,導致你整個人腦子嗡嗡響
- 心中怒氣翻湧,找到產品說
- 回來,坐在椅子上,心裡想:
- 老夫寫的程式碼天衣無縫,這什麼幾把需求
- 可惡,這次測試,起碼要給我多提十幾個BUG
- 克蘇魯山開始猙獰
產品飄來,加改需求:如此,如此,,,這般,這般,,,
- 你....
產品:改下,,,然後,扔給你幾十頁的PRD
你看了看這改了幾十版的克蘇魯山,這幾十個彈窗邏輯居然都寫在一個檔案裡,快一萬行的程式碼。。。
- 心裡不禁想:
- 本帥比寫的程式碼果然牛批,或許這就是藝術!藝術總是曲高和寡,難被人理解!而我的程式碼更牛批,連我自己都看不懂了!
- 這程式碼行數!這程式碼結構!不得拍個照留念下,傳給以後的孩子當傳家寶供著!
- 心裡不禁嘚瑟:
- 這塊業務,除了我,還有誰敢動,成為頭兒的心腹,指日可待!
- 但,轉念深思後:事了拂衣去,深藏功與名
重構
隨著業務的逐漸複雜,最初的設計缺點會逐漸暴露;重構有缺陷的程式碼流程,變得勢在必行,這會極大的降低維護成本
如果心中對責任鏈模式有一些概念的話,會發現上面的業務,極其適合責任鏈模式!
對上面的業務進行分析,可以明確一些事
- 這個業務是一個鏈式的,有著明確的方向性:單向,從頭到尾指向
- 業務拆分開,可以將一個彈窗作為單顆粒度,一個彈窗作為節點
- 上級的業務節點可以對下級節點攔截(點選取消,拒絕按鈕,不再進行後續業務)
重構上面的程式碼,只要明確思想和流程就行了
第一稿業務
- 業務流程
- 責任鏈
- 程式碼:簡寫
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("測試攔截器");
}
複製程式碼
第二稿業務
- 業務流程
- 責任鏈
- 程式碼:簡寫
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("測試攔截器");
}
複製程式碼
第三稿業務
- 業務流程
- 責任鏈
- 程式碼:簡寫
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("測試攔截器");
}
複製程式碼
總結
經過責任鏈模式重構後,業務節點被明確的區分開,整個流程從程式碼上看,都相當的清楚,維護將變的異常輕鬆;或許,此時能感受到一些,程式設計的樂趣了
花樣彈窗業務
業務描述
來描述一個新的業務:這個業務場景真實存在某辦公軟體
- 進入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(
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 {
/// span: 設定該引數,可控跨越多級節點
/// 預設0,則不跨越節點(遍歷所有節點)
next(dynamic data, {int span = 0});
}
///實現init處理器
class _InterceptSingleHandler extends SingleHandler {
List<InterceptSingle> intercepts;
int index;
_InterceptSingleHandler({
required this.index,
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(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 {
/// span: 設定該引數,可控跨越多級節點
/// 預設0,則不跨越節點(遍歷所有節點)
next(dynamic data, {int span = 0});
}
///實現init處理器
class _TwiceInitHandler extends TwiceHandler {
List<InterceptTwice> intercepts;
int index;
_TwiceInitHandler({
required this.index,
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({
required this.index,
required this.intercepts,
});
@override
next(dynamic data, {int span = 0}) {
if ((index + span) >= intercepts.length) {
return;
}
var intercept = intercepts[index + 1];
var handler = _TwiceSubmitHandler(
index: index + (span + 1),
intercepts: intercepts,
);
intercept.onSubmit(data, handler);
}
}
複製程式碼
最後
第一次,寫這種結合業務的文章
如有收穫,還請點個贊,讓我感受一下,各位是否讀有所獲~~
感謝閱讀,下次再會~~