本文書接上回《懂了這個道理,人月神話不再是神話!》,關注公眾號(老肖想當外語大佬)獲取信息:
- 最新文章更新;
- DDD框架源碼(.NET、Java雙平台);
- 加羣暢聊,建模分析、技術交流;
- 視頻和直播在B站。
關注公眾號一定要星標,以及時獲得最新推送。
背景
關於“複雜度”我在系列開篇《關於領域驅動設計,大家都理解錯了》中就有所剖析,然而在與大家交流的過程中發現,很多對於軟件設計決策的分析,最底層的觀點衝突來自與對“複雜度”理解的衝突,大家的結論都是構建在它上面的,因此,在這裏全面地展示我對於“複雜度”的理解,以期與大家能夠在更深的層次上識別共識,明晰分歧。
軟件成本與複雜度
我們在設計軟件系統的過程中,有一個貫穿始終的約束條件,就是“軟件成本”,時間成本、人力成本等等資源,都是有限的,衡量一支團隊的效率,同樣也可以用“軟件成本”來作為計算依據,而軟件成本,我認為最核心的因素就是軟件的“複雜度”,如果“複雜度”是一個有刻度的尺子,那麼我認為,複雜度越高,軟件成本越高。而我的軟件設計決策的所有依據,都構建在這個起點。如果我們期望降低軟件成本,那麼就意味着,我們需要降低軟件的複雜度,那麼理解複雜度就是我們的必經之路。
理解複雜度
要理解複雜度,我們不得不先回答一個問題:如何判斷“複雜”和“簡單”?我們先來看幾個例子,下面兩個系統,你覺得哪個更復雜?我想大部分人一定會選“系統2”,因為顯然它的元素數量更多。
如果我們的系統元素之間存在一些關係,那麼下面兩個系統呢?是不是感覺“系統3”更復雜?
基於上面的例子,我們更極端地設想一下,假如你有一個系統A,有100個獨立的模塊,這些模塊之間沒有相互聯繫,另外一個系統B,有10個模塊,這些模塊之間相互會有影響。我們假設這裏的一個個“模塊”本身複雜度相同,那麼你認為系統A和系統B哪個更復雜?對我而言,我會堅定地認為系統B更復雜。
基於上面的例子,我得出了兩個判斷:
- 系統複雜度與元素的數量和元素的關係有關;
- 元素的關係對系統複雜度的影響遠遠大於元素的數量所產生的影響;
基於上述的觀點,我們就可以根據元素數量、元素關係這兩個要素來衡量“複雜度”,也就是説我們可以有一個判斷複雜度的標尺,這個標尺有兩個重要參數:元素數量、元素關係。
我們知道,不可衡量則不可改進,而有了這個標尺,也意味着我們可以做衡量,可以對決策方案的優劣取捨做判斷,這就是我們能夠持續改進的起點。
尊重複雜度
經典的複雜度守恆定律表示,“複雜度不可被消除,只可被轉移”,我深以為然,回到軟件設計領域,我們的複雜度具體在哪裏呢?我認為我們的軟件系統複雜度的根源,來自於需求本身的複雜度,它決定了最終解決方案複雜度。
既然複雜度無法被消除,那我們是不是可以推導得出,解決方案的複雜度,最小等於需求的複雜度?那麼我們在設計方案的過程中,要做的,就是儘可能避免引入額外的複雜度。這個過程,系統元素數量和元素關係成為我們關注的要點,而且元素關係是其中的重中之重。
在我們分析需求,給出解決方案的過程中,複雜度由兩方面組成,一個是業務邏輯本身的複雜度,一個是引入的技術複雜度,其中業務邏輯是來自需求,不能打折扣,因此這部分的複雜度是無法被消除的,這也就是為什麼我所説的“尊重複雜度”,尊重業務固有複雜度,不要嘗試消除它,我們能做的,僅僅是將它的複雜度儘可能封閉在一個個“收納箱”裏,這些收納箱,就是我們常説的“領域”。
而對於“技術複雜度”,我們要做的就是避免過度設計,避免引入額外的複雜技術,避免牛刀殺雞,儘可能採取與需求匹配的技術解決方案。
如果你瞭解“熵”的概念,也許會發現,我們在討論複雜度邏輯,與“熵增定律”不謀而合,而軟件系統的複雜度也是隨着我們一步步迭代走向更加複雜和混亂的,我們能做的就是儘可能維持複雜度的增速儘可能地低,使得系統複雜度儘可能在我們所能掌控的範圍內。
因此,尊重複雜度,也是對自然規律的尊重。
掌控複雜度
那麼,在我們設計和實現軟件系統的過程中,該怎麼做呢?既然我們知道複雜度與元素數量和元素關係有關,而元素關係又是複雜度的核心來源,那麼意味着我們設計的策略可以是:
- 儘可能避免引入元素關係;
- 實在不行,用元素數量來置換元素關係,也是划算的;
這些策略,體現在具體的軟件系統建模中,具像化出來就是:
- 保持模型(聚合)之間的邊界不被打破;
- 如果一個需求現有模型無法滿足,就考慮創建一個新的模型,而不是把原有的多個模型耦合在一起;
要掌控關係,我們需要具體到代碼層面,識別什麼是關係,我認為有下面特徵的代碼都屬於關係:
- 聚合類型之間的相互引用;
- 一個類型,同時對多個聚合類型產生操作或影響;
基於上述認知,我們就得出一些代碼原則:
- 模型(聚合)之間不相互依賴;
- 如果現有模型(聚合)無法滿足需求,就創建一個新的模型(聚合);
- 模型之間的相互影響,通過“事件”傳遞信息,從而實現數據最終一致;
關於具體的代碼怎麼寫,我在《DDD建模後寫代碼的正確姿勢(Java、dotnet雙平台)》一文中有一些示例展示,也歡迎大家參與我們DDD實戰項目d3shop,一起體驗掌控複雜度的實踐,具體參見《歡迎加入d3shop,一個DDD實戰項目》。
後續
本篇是從一個平面的視角來解析複雜度,探討的是元素之間的關係對複雜度的影響,而一個複雜的系統,並不是簡單的一層,在不同的層次,進行展開,又可以看作是一個子系統,下篇我們將從縱深的視角來剖析複雜度與領域驅動設計思想的相互關聯。