> 作為一個研發,我們工作中都會處理面臨下面這些困惑:
> 1. 又加需求,方法本來處理了 300 行,現在又加 50 行。
> 2. 狀態邏輯太多了,產品第 2 期又加了一個邏輯,代碼結果要調整,很頭疼。
> 3. 每個人都在吐槽,業務研發在工作中處理最多的是 `if else` ,好不容易寫個 `switch` 都能給同事吹一周。
以上三個場景應該是日常需求迭代優化中面臨最多的場景了,作為一個自稱編碼水平較高的人,總結了以下三個真實的場景,給出一些可選的方案。
## **第一板斧:抽象事件,驅動業務**
### **核心**
梳理產品邏輯中的**主流程節點**,整理節點所**需要的依賴數據**以及節點**觸發后對應的業務邏輯**。
類比消息隊列,也是不同的業務方訂閱自己的事件源,進行不同的處理。不同點在于一個是分布式,一個是本文描述單機業務場景。
### **實際例子**
舉一個用戶注冊之后的場景,需要:
1. 發短信
2. 發優惠券
如果用戶注冊成功之后,直接發 MQ 消息,那么用戶系統和券系統分別訂閱這個消息進行處理。不過這里討論的是在**一個項目模塊中處理完所有相關的邏輯**。
代碼將在`UserRegistered()`中一步一步去處理邏輯,之后需求又加入了**初始化 A 數據**和**初始化 B 數據**兩個需求,實現也會落到這個方法之后,最后整個代碼會越來越臃腫。
:-: 
接下來用時間訂閱模型去化解這個點。
最后對應到程序代碼可能是這樣的:
~~~php
// 事件注冊
Event::register(UserRegistered::class, [
SendSms::class, // 發短信
SendCoupon::class, // 發優惠券
InitSystemA::class, // 初始化A系統
InitSystemB::class, // 初始化B系統
])
---------------------------------------------------------------------------------------
// 業務調用
handleRegistered($user) {
$event = new UserRegistered($user);
$event->fire();
}
~~~
后面的迭代維護中,只要主流程不發生變化,那么相應的邏輯只需要去增加訂閱者去實現。
:-: 
## **第二板斧:有限狀態機,定義流程**
在業務邏輯數據處理這一層,很多的業務場景都與數據扭轉狀態有關,并且最后會有相應的數據實體映射。比如我們常見的:
1. 各種商品訂單(天貓,淘寶,外賣)
2. 工作流(審批,工單處理)
這類需求的特點:讀寫場景 `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 個字段來表達我們的數據模型。
那一個訂單舉例子,我們有如上訂單系統。不可避免,每一個業務場景,每一個邏輯,產品邏輯都可能有自己的配置和相應的處理流程,且這些邏輯都是業務迭代優化的重災區,比如:
1. 江浙滬地區包郵
2. 某一批固定的城市需要打 8.8 折
3. 雨天調價格
4. 法定節假日打樣不服務
5. VIP 身份的用戶展示文案特殊處理
每一個開發同學都曾被這些邏輯折磨的異常人痛苦,這里給出一個抽象的方案,最終每一個訂單特征都會落到具體的業務處理類,所有的類都會實現改業務場景的 `interface`,主流程只需要構造 n元祖然后獲取到相應的 interface之后進行調用。
:-: 
n 元祖的概念其實早已經滲透到了開發中的每一個角落,我們需要做的事情就是,在業務開發的時候真正的去思考這一層數據模型,然后加以運用,最后的代碼一定不那么 `if else`。
## **總接**
以上三板斧在業務中使用可以很輕,也可以很重。這就意味著我們能自己寫一個簡單夠用的(對于你完全了解成長有限的業務場景),或者找一個 star 多且在維護的開源方案(對于有潛力,未來大有可為的業務)來代替。同時,這些編碼套路在各種場景下都能非常靈活的組合,比如:
1. 訂單狀態更新后觸發事件(狀態機 + 事件訂閱)
2. 不同業務線,狀態機配置,初始化放松不同(n 元組 + 狀態機)
不得不提到的一點,在漫長的業務迭代中,產品文檔會越來越缺失,最終只有通過代碼才能了解線上的真正邏輯(有時代碼過于復雜,可能就沒有人知道線上的具體情況了),集中配置,統一維護的意義之一就在于此。相信做過復雜歷史系統的交接或重構的同學對這一點都深有體會。
<br>
<br>
> 原文地址:https://learnku.com/articles/39302#replies
- PHP
- PHP 核心架構
- PHP 生命周期
- PHP-FPM 詳解
- PHP-FPM 配置優化
- PHP 命名空間和自動加載
- PHP 運行模式
- PHP 的 Buffer(緩沖區)
- php.ini 配置文件參數優化
- 常見面試題
- 常用函數
- 幾種排序算法
- PHP - 框架
- Laravel
- Laravel 生命周期
- ThinkPHP
- MySQL
- 常見問題
- MySQL 索引
- 事務
- 鎖機制
- Explain 使用分析
- MySQL 高性能優化規范
- UNION 與 UNION ALL
- MySQL報錯:sql_mode=only_full_group_by
- MySQL 默認的 sql_mode 詳解
- 正則表達式
- Redis
- Redis 知識
- 持久化
- 主從復制、哨兵、集群
- Redis 緩存擊穿、穿透、雪崩
- Redis 分布式鎖
- RedisBloom
- 網絡
- 計算機網絡模型
- TCP
- UDP
- HTTP
- HTTPS
- WebSocket
- 常見幾種網絡攻擊方式
- Nginx
- 狀態碼
- 配置文件
- Nginx 代理+負載均衡
- Nginx 緩存
- Nginx 優化
- Nginx 配置 SSL 證書
- Linux
- 常用命令
- Vim 常用操作命令
- Supervisor 進程管理
- CentOS與Ubuntu系統區別
- Java
- 消息隊列
- 運維
- RAID 磁盤陣列
- 邏輯分區管理 LVM
- 業務
- 標準通信接口設計
- 業務邏輯開發套路的三板斧
- 微信小程序登錄流程
- 7種Web實時消息推送方案
- 用戶簽到
- 用戶注冊-短信驗證碼
- SQLServer 刪除同一天用戶重復簽到
- 軟件研發完整流程
- 前端
- Redux
- 其他
- 百度云盤大文件下載
- 日常報錯記錄
- GIT
- SSL certificate problem: unable to get local issuer certificate
- NPM
- reason: connect ECONNREFUSED 127.0.0.1:31181
- SVN
- SVN客戶端無法連接SVN服務器,主機積極拒絕
- Python
- 基礎
- pyecharts圖表
- 對象
- 數據庫
- PySpark
- 多線程
- 正則
- Hadoop
- 概述
- HDFS