做者 | 張建飛 阿里巴巴高級技術專家設計模式
導讀:針對業務在不一樣場景下的差別,咱們經常會習慣性地使用 if-else 來實現不一樣的業務邏輯,長此以往代碼愈來愈難以維護。那麼如何消除這些 if-else?面對複雜業務應如何思考和分析?本文分享阿里高級技術專家張建飛(Frank)關於複雜業務治理的方法論,介紹一種多維度分析問題的方法:矩陣分析法。You should not be a if-else coder, should be a complexity conquer.
——Frank框架
這篇文章,是對以前我在《阿里高級技術專家方法論:如何寫複雜業務代碼?》說的「自上而下的結構化分解 + 自下而上的抽象建模」方法論的升級。由於在以前的方法論中,咱們缺乏一個多維度看問題的視角,這種維度思惟的缺失,可能會致使 miss 掉一些重要的業務信息,從而使咱們制定軟件設計策略的時候,陷入困難。less
有了維度思惟,咱們即可以更加方面的去看清業務的全貌,更加全面的掌握業務信息,從而幫助咱們更加體系化的去治理複雜性。微服務
我常常說,咱們不要作一個 if-else coder。這裏的 if-else,不是說咱們在 coding 的時候不能使用 if-else,而是說咱們不該該簡陋地用 if-else 去實現業務的分支流程,由於這樣隨意的代碼堆砌很容易堆出一座座「屎山」。學習
業務的差別性是 if-else 的根源。以零售通的商品業務爲例。不一樣的處理場景,其業務邏輯實現是有差別性的。以下圖所示,商品業務的差別性,主要體如今商品類型、銷售方式和倉儲方式的不一樣。測試
這三個維度上的差別組合起來,有 2 3 2 = 12 之多。這就是爲何在老代碼中,處處能夠看到 if(組合品) blabla,if(贈品) blabla,if(實倉) blabla 之類的代碼。ui
那麼,要如何消除這些討厭的 if-else 呢?咱們能夠考慮如下兩種方式:編碼
多態擴展能夠有繼承和組合兩種方式。繼承勿用多言,組合有點像策略模式,也就是把須要擴展的部分封裝、抽象成須要被組合的對象,而後對其進行擴展,好比星環的能力擴展點就是這種方式。spa
這裏,咱們舉一個繼承的例子,商品在上架的時候要檢查商品的狀態是否可售,普通商品(Item)檢查本身就行了,而組合商品(CombineItem)須要檢查每個子商品。設計
用過程式編碼的方式,很容易就能寫出以下的代碼:
public void checkSellable(Item item){ if (item.isNormal()){ item.isSellable(); //省略異常處理 } else{ List<Item> childItems = getChildItems(); childItems.forEach(childItem -> childItem.isSellable()); //省略異常處理 } }
然而,這個實現不優雅,不知足 OCP,也缺乏業務語義顯性化的表達。更好的作法是,咱們能夠把 CombineItem 和 Item 的關係經過模型顯性化的表達出來。
這樣一來,一方面模型正確的反應了實體關係,更清晰了。另外一方面,咱們能夠利用多態來處理CombineItem和Item的差別,擴展性更好。重構後,代碼會變成:
public void checkSellable(Item item){ if (!item.isSellable()){ throw new BizException("商品的狀態不可售,不能上架"); } }
所謂的代碼分離是指,對於不一樣的業務場景,咱們用不一樣的編排代碼將他們分開。以商品上架爲例,咱們能夠這樣寫:
/** * 1. 普通商品上架 */ public void itemOnSale(){ checkItemStock();//檢查庫存 checkItemSellable();//檢查可售狀態 checkItemPurchaseLimit();//檢查限購 checkItemFreight();//檢查運費 checkItemCommission();//檢查佣金 checkItemActivityConflict();//檢查活動衝突 generateCspuGroupNo();//生成單品組號 publishItem();//發佈商品 } /** * 2. 組合商品上架 */ public void combineItemOnSale(){ checkCombineItemStock();//檢查庫存 checkCombineItemSellable();//檢查可售狀態 checkCombineItemPurchaseLimit();//檢查限購 checkCombineItemFreight();//檢查運費 checkCombineItemCommission();//檢查佣金 checkCombineItemActivityConflict();//檢查活動衝突 generateCspuGroupNo();//生成單品組號 publishCombineItem();//發佈商品 } /** * 3. 贈品上架 */ public void giftItemOnSale(){ checkGiftItemSellable();//檢查可售狀態 publishGiftItem();//發佈商品 }
這種方式,固然也能夠消除 if-else,彼此獨立,也還清晰。但複用性是個問題。
細心的你可能已經發現了,在上面的案例中,普通商品和組合商品的業務流程基本是同樣的。若是採用兩套編排代碼,有點冗餘,這種重複將不利於後期代碼的維護,會出現散彈式修改(一個業務邏輯要修改多處)的問題。
一個極端狀況是,假如普通商品和組合商品,只有 checkSellable() 不同,其它都同樣。那毫無疑問,咱們使用有多態(繼承關係)的 CombineItem 和 Item 來處理差別,會更加合適。
而贈品上架的狀況偏偏相反,它和其餘商品的上架流程差別很大。反而不適合和他們合用一套流程代碼,由於這樣反而會增長他人的理解成本。還不如單獨起一個流程來的清晰。
那麼,問題來了,咱們何時要用多態來處理差別,何時要用代碼分離來處理差別呢?
接下來,是我今天要給你着重介紹的多維度分析問題的方法論之一:矩陣分析法。
咱們能夠弄一個矩陣,縱列表明業務場景,橫列表明業務動做,裏面的內容表明在這個業務場景下的業務動做的詳細業務流程。對於咱們的商品業務,咱們能夠獲得以下的矩陣:
經過上面的矩陣分析,咱們不難看出普通品和組合品能夠複用同一套流程編排代碼,而贈品和出清品的業務相對簡單,更適合有一套獨立的編排代碼,這樣的代碼結構會更容易理解。
上面的案例不是我編造出來的,而是我在和張文(我同事)討論應該用哪一種方式去處理業務差別的真實故事。
我記得在和大學討論完,開車回去的路上,我一直在想這個問題,而後在第二個路口等紅燈的時候,忽然有一個靈感冒出來。我抑制不住興奮,一邊開車,一邊發消息給張文說:「我想到了一個很 NB 的方法論,能解決在‘多態擴展’和‘代碼分離’之間如何作選擇的問題」。
其實,我知道我興奮的不只僅是解決了這個問題。我興奮的是,我第一次真正領悟到了多維度思考的重要性。從而有機會從一個「單維度」生物,升級成一個「多維度」思考者。媽媽不再用擔憂我被「降維打擊」了 :)
結構化思惟有用、頗有用、很是有用,只是它更多關注的是單向維度的事情。好比我要拆解業務流程,我要分解老闆給個人工做安排,我要梳理測試用例,都是單向維度的。
而複雜性,一般不只僅是一個維度上的複雜,而是在多個維度上的交叉複雜性。當問題涉及的要素比較多,彼此關聯關係很複雜的時候,兩個維度確定會比一個維度要來的清晰,這也是爲何說矩陣思惟是比結構化思惟更高層次的思惟方式。
實際上,咱們從漢語的詞彙上,也不難看出一我的的思惟層級,是和他的思考維度正相關的。當咱們說這我的很「軸」、「一根筋」的時候,其實是在說他只有一維的線性思惟。因此,觀察事物的視角越多,維度越豐富,其思惟層級也會越高。
有了這些感悟,我開始系統的整理關於多維度思考分析的資料,發現這種思考方式真是無處不在。發現的越多,我越是感慨,爲何如此重要的思惟方式,我到如今才領悟到。
好比,在作產品分析的時候,有對產品發展前景進行分析的波士頓矩陣。
當年,我在 1688 作交易下單業務的時候,有很是多的下單場景,每種場景下,買家享受的權益是不同的(以下表所示)。咱們當時也是使用了矩陣去表達這個複雜的關係,只是當時尚未想到要將其提高到方法論的高度。
在數據分析中,維度分析是很是重要的,特別是維度不少的時候,咱們能夠經過皮爾遜積矩相關係數,作交叉分析,從而彌補獨立維度分析無法發現的一些問題。
簡單相關係數矩陣
最近我碰巧看到 Alan Shalloway 寫的《設計模式解析:Design Patterns Explained》,這是一本很是經典的關於 OOP 的書,裏面的第十六章就是專門講「分析矩陣」的,做者創造這個方法論的初衷也是由於業務涉及的要素太多,信息量太大,他須要一種組織海量數據的新方式。
我和 Alan 的路徑不同,可是都得出了一樣的結論。因而可知,這種矩陣分析的方式的確是對複雜業務進行分析的一把利器,業務場景越多,交叉關係越是複雜,越須要這樣的分析。
生產關係決定生產力,對於一個管理者來講,如何有效的設置組織結構是決定團隊是否能高效協做的關鍵。因此咱們能夠看到公司裏面,每一年都有比較大的關於組織結構和人員安排的調整。
對於技術團隊來講,咱們習慣於按領域劃分工做範圍,這樣作的好處是責任到人、職責清晰。然而,領域只是一個維度,咱們工做一般都是以項目的形式的開展,而項目一般是貫穿多個領域的。因此,在作團隊組織規劃的時候,咱們能夠經過業務領域和業務項目兩個維度去看。
好比,在我負責的商品團隊,我會按照以下的形式去作職責劃分。
除了工做,生活中也處處可見多維思考的重要性。
好比,咱們說浪費可恥,應該把盤子舔的很乾淨,豈不知加上時間維度以後,你當前的舔盤,後面可能要耗費更多的資源和精力去減肥,反而會形成更大的浪費。
咱們說代碼寫的醜陋,是由於要「快速」支撐業務,加上時間維度以後,這種臨時的妥協,換來的是意想不到的 bug,線上故障,以及無止盡的 996。
簡單的思考是「點」狀的,好比舔盤、代碼堆砌就是當下的「點」;好一點的思考是「線」狀,加上時間線以後,不難看出「點」是有問題的;再全面一些的思考是「面」(二維);更體系化的思考是「體」(三維);好比,RFM 模型就是一個很不錯的三維模型。惋惜的是,在表達上,咱們人類只能在二維的空間裏去模擬三維,不然四維可能會更加有用。
在前言部分,我已經說過了,多維分析是對以前方法論的升級。加上之前的方法論,完整的方法論應該是「業務理解-->領域建模-->流程分解-->多維分析」。
爲了方便你們理解,下面我把這些方法論作一個簡單的串聯和解釋。
理解業務是全部工做的起點。首先,咱們要找到業務的核心要素,理解核心概念,梳理業務流程。
好比,在零售通的商品域,咱們要知道什麼是商品(Item),什麼是單品(CSPU),什麼是組合品(CombineItem)。在下單域,咱們要知道訂單(order)的構成要素是商品、優惠、支付。在 CRM 領域,咱們要理解客戶、機會、聯繫人、Leads 等等。
這裏,我想再次強調下語言的重要性,語言是咱們思考的載體,就像維特根斯坦說的:「凡是可以說的事情,都可以說清楚」。
你不該該放過任何一個模糊的業務概念,必定要透徹的理解它,並給與合理的命名(Ubiquitous Language)。惟有如此,咱們才能更加清晰的理解業務,才能更好的開展後續的工做。
在軟件設計中,模型是指實體,以及實體之間的聯繫,這裏須要咱們具有良好的抽象能力。可以透過龐雜的表象,找到事務的本質核心。
再複雜的業務領域,其核心概念都不該該太複雜,抓住了核心,咱們就抓住了主線,業務每每都是圍繞着這些核心實體展開的。
好比,商品域雖然很複雜,但其核心的領域模型,無外乎就以下圖所示:
關於流程分解,在《阿里高級技術專家方法論:如何寫複雜業務代碼?》裏面已經有很是詳細的闡述,這裏就不贅述了。
簡單來講,流程分解就是對業務過程進行詳細的分解,使用結構化的方法論(先演繹、後概括),最後造成一個金字塔結構。
好比,在商品領域,有建立商品、商品上架、上架審覈、商品下架、下架審覈、修改商品、刪除商品等一些列動做(流程),每一個動做的背後都有很是複雜的業務邏輯。咱們須要對這些流程進行詳細的梳理,而後按步驟進行分解。最後造成一個以下的金字塔結構:
關於多維分析,我以二維的矩陣分析爲例,我想我前面應該已經說清楚了。
業務的複雜性主要體如今流程的複雜性和多維度要素相互關聯、依賴關係上,結構化思惟能夠幫咱們梳理流程,而矩陣思惟能夠幫忙咱們梳理、呈現多維度關聯、依賴關係。兩者結合,能夠更加全面的展示覆雜業務的全貌。從而讓咱們的治理能夠有的放矢、有章可循。
既然是方法論,在這裏,我會嘗試給出一個矩陣分析的框架。試想下,若是咱們的業務很簡單,只有一個業務場景,沒有分支流程。咱們的系統不會太複雜。之因此複雜,是由於各類業務場景互相疊加、依賴、影響。
所以,咱們在作矩陣分析的時候,縱軸能夠選擇使用業務場景,橫軸是備選維度,能夠是受場景影響的業務流程(如文章中的商品流程矩陣圖),也能夠是受場景影響的業務屬性(如文章中的訂單組成要素矩陣圖),或者任何其它不一樣性質的「東西」。
經過矩陣圖,能夠清晰的展示不一樣場景下,業務的差別性。基於此,咱們能夠定製知足差別性的最佳實現策略,多是多態擴展,多是分離的代碼,也多是其它。
這就是矩陣分析的要義,其本質是一種多維度思考的方法論。
最後,我想說世界是熵增的(即萬物都在緩慢的分崩離析),控制複雜度是咱們這些從業者沒法推卸的責任和使命。
軟件行業的發展才幾十年,仍是一門年輕的學科,軟件工程就像一個剛學會走路的小孩,還很不成熟,有時還很幼稚。
但畢竟仍是有幾十年的沉澱,仍是有一些好的方法和實踐能夠參考,個人這些總結沉澱只是在前人的基礎上,多走了一點點而已。但就是這一點點,也實屬來自不易,其中冷暖,只有本身能體會。能夠說,這一路走來,是一場對心力、腦力和體力的持續考驗。
梳理清晰了,再配合 COLA(https://start.aliyun.com/)的指導,咱們就有可能寫出清晰、易讀的代碼,就有可能從一個 if-else coder 升級爲一個 complexity conquer。
而這不正是咱們工程師孜孜不倦的追求嗎?
「 阿里巴巴雲原生關注微服務、Serverless、容器、Service Mesh 等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,作最懂雲原生開發者的公衆號。」