轉自:代碼這件大事
背景:
作爲一個研發,我們工作中都會處理面臨下面這些困惑:
又加需求,一個方法本來就處理了 300 行,現在又加 50 行。
狀態邏輯太多了,產品第 2 期又加了一個邏輯,代碼結構要調整,很頭疼。
每個人都在吐槽,業務研發在工作中處理最多的就是 if else,好不容易寫個 switch 都能給同事吹一週。以上三個場景應該是日常需求迭代優化中面臨最多的場景了,作爲一個自稱編碼水平較高的人,總結了以下三個真實的場景,給出一些可選的方案。
第一板斧:抽象事件,驅動業務
核心
梳理產品邏輯中的主流程節點,整理節點所需要的依賴數據已經節點觸發後對應的業務邏輯。類比消息隊列,也是不同的業務方訂閱自己的事件源,進行不同的處理。不同點在於一個是分佈式,一個是本文描述單機業務處理場景。
實際例子
舉一個用戶註冊之後的場景,需要:
發短信
發優惠券 如果用戶註冊成功之後,直接發了mq消息,那麼用戶系統和券系統分別訂閱這個消息進行處理。不過這裏討論的是在一個項目模塊中處理完所有相關的邏輯。
代碼將在 UserRegistered()
中一步一步去處理邏輯,之後需求又加入了 初始化A數據和初始化B數據 兩個需求,實現也會落到這個方法之後,最後整個代碼會越來越臃腫。
接下來用事件訂閱模型去化解這個點,非常實用,一點都不華麗,代碼也很好讀懂。
最後對應到程序代碼可能是這樣的:
# 事件註冊
Event::register(UserRegistered::class, [
SendSms::class,
SendCoupon::class,
InitSystemA::class,
InitSystemB::class,
]);
# 業務調用
handleRegistered($user) {
$event = new UserRegistered($user);
$event->fire();
}
後面的迭代維護中,只要主流程不發生變化,那麼相應的邏輯只需要去增加訂閱者去實現。
第二板斧:有限狀態機,定義流程
在業務邏輯數據處理這一層,很多的業務場景都與數據扭轉狀態有關,並且最後會有相應的數據實體相映射。比如我們常見的:
各種商品訂單(天貓,淘寶,外賣)
工作流(審批,工單處理)
這類需求的特點是,讀寫場景QPS不高,對數據的準確一致性要求非常高。我們底層一般直接存儲到數據庫,之上加一層簡單的數據緩存就能處理。
面臨的主要問題是,狀態太多難以維護,應該還會出現狀態的調整比如特殊場景下的狀態A到狀態Z的扭轉。
不過業內早已給出了比較通用的解決方案,有限狀態機。下面我們列舉一個簡單的訂單狀態扭轉邏輯:
fsm := fsm.NewFSM(
"created",
fsm.Events{
{Name: "pay", Src: []string{"created"}, Dst: "paid"}, //支付
{Name: "cancel", Src: []string{"created"}, Dst: "closed"}, //關閉
},
fsm.Callbacks{
"after_pay": func(e *fsm.Event) { /* 支付成功調用 */ },
},
)
如果設計到狀態相關的調整,在狀態機定義的地方去修改就可以解決問題。和事件訂閱非常相識,也是集中維護,統一管理。
第三板斧:n元組配置,組合輸出
軟件工程沒有銀彈 --布魯克斯
軟件開發中我們遇到的一個一個需求都是不可預測的,我們確實很難找到一種終極的解決方案。不過本文中的討論侷限在業務邏輯開發的開發套路。那麼,n元組這個簡單的概念可能算得上一顆銀彈。不管業務邏輯有多複雜,在理論上我們都能抽象出n個字段來表達我們的數據模型。
拿一個訂單舉例子,我們有如上訂單特徵。不可避免的,每一個業務場景,每一個邏輯,產品邏輯都可能有自己的配置和相應的處理流程,且這些邏輯都是業務迭代優化的重災區,比如:
江浙滬地區包郵
某一批固定的城市需要打8.8折
雨天調價格
法定節假日打烊不服務
vip身份的用戶展示文案特殊處理
每一個開發同學都曾被這些邏輯折磨的異常痛苦,這裏給出一個抽象的方案,最終每一個訂單特徵都會落到具體的業務處理類,所有的類都實現該業務場景的 interface,主流程只需要構造 n元組然後獲取到相應的 interface 之後進行調用。
n元組的概念其實早已經滲透到了開發中的每一個角落,我們需要做的事情就是,在業務開發的時候真正的去思考這一層數據模型,然後加以運用,最後的代碼一定不那麼 if else。
總結
以上三板斧在業務中使用可以很輕,也可以很重。這就意味着我們能自己寫一個簡單夠用的(對於你完全瞭解成長有限的業務場景),或者找一個star多且在維護的開源方案(對於有潛力,未來大有可爲的業務)來代替。同時,這些編碼套路在各種場景下都能非常靈活的組合,比如:
訂單狀態更新後觸發事件(狀態機+事件訂閱)
不同業務線,狀態機配置,初始化放鬆不同(n元組+狀態機)
不得不提到的一點,在漫長的業務迭代中,產品文檔會越來越缺失,最終只有通過代碼才能瞭解線上的真正邏輯(有時間代碼過於複雜,可能就沒有人知道線上的具體情況了),集中配置,統一維護的意義之一就在於此。相信做過復雜歷史系統的交接或重構的同學對這一點都深有體會。
參考
[有限狀態機](https://zh.wikipedia.org/zh-hans/%E6%9C%89%E9%99%90%E7%8A%B6%E6%80%81%E6%9C%BA)
[go fsm](https://github.com/looplab/fsm)
熱文推薦
如有收穫,點個在看,誠摯感謝