軟體設計的複雜度

2021-09-08 18:31:25 字數 2821 閱讀 3861

軟體技術發展的使命之一就是控制複雜度(complexity)。從高階語言的產生,到結構化程式設計,再到物件導向程式設計、元件化程式設計等等。關於複雜度的定義並不一致,想要詳細了解的可以讀讀the many faces of complexity in software design.

英文中complex和complicated有著微妙的不同。但總結起來,軟體複雜度偏負面意義,包括兩個要點: 

- 難以理解 (難以維護和擴充套件。) 

- 無法**行為 

複雜度是隨著軟體規模不斷擴大而必然產生的。它本身又是乙個相對的概念,同乙個系統對於設計者、開發者,以及維護者而言,複雜度是不同的。不同時期,乙個程式設計師所能掌握的複雜度也是不同的,這也是乙個程式設計師不斷提公升的目標。

既然業界已經對抗複雜度幾十年了,我們就來整理一下。

以分解的方式進行的設計,主要特點是: 

- 分離職責(seperation of concerns,參考單一職責原則) 

- 關注介面(定義互動)

這是最常使用的技術了。將乙個大問題,不斷的拆解為各個小問題進行分析研究,然後再組合到一起。在西方稱為divide and conquer principle (分而治之原則)。

在結構化程式設計的時代,提倡模組化(modularization)。最早提出軟體複雜度的工程師提出了基於元件的軟體(component based software)。不知道是不是從樂高積木上得到的啟發,將系統中拆分為不同的元件,各自實現,然後再組裝在一起。

在架構設計中,無論是c/s風格,分層,還是n-tier,soa,和前面元件式一樣,都是在進行分解,它們都更加強調組合互動。設計上,分分職責,定義好介面,就可以各自開發了。然後將互動限定於介面層,就能夠很好的控制整個系統的複雜度。

比如應用層使用乙個語音庫(speech library,乙個以庫的形式的模組化應用), 根本不用關心其內部實現,只要了解如何使用它的api就可以了。

改進依賴關係的要點: 

- 無環形依賴 

- 穩定依賴原則(sdp)

分解可以降低系統層級的複雜度,但還有一種複雜度無法解決,即依賴的問題。這在敏捷軟體開發:原則、模式與實踐中關於依賴性的討論很詳細。當參與者增加時,互動就會隨之變得複雜。而當前的軟體規模,系統中的各類sdk的api, framework的api, 各種第三方庫越來越多,模組間的依賴就會越來越複雜。 

顯然系統中的模組或者元件太多了,需要進一步整理。但真正的問題在於出現了雙向和環形的依賴。比如上圖中負責計算的computing模組也依賴到了ui模組,或許是因為ui層持有乙個計算所需的關鍵引數。如果ui層變更,就可能會影響到computing,出現無法**的行為,給客戶以不穩定的印象。

所以模組間的依賴關係必須簡化,絕對不能出現環形的依賴。以chromium為例,它對各個模組的依賴就有嚴格的定義,並且有deps在編譯期保證程式設計師不會犯錯。下圖是chromium component依賴關係的定義,其中component內部目錄的依賴關係也有定義: 

當底層模組需要依賴上層模組的實現時,就要通過依賴倒置(dip)來處理。簡單而言就是由底層模組定義乙個介面,要求上層模組實現並注入到底層模組。 

人的學習過程最有效的一種方式就是歸類,其中運用的就是抽象思維。面對變幻無常的天氣,人類通過對雲的形狀進行抽象,就可以**天氣變化。這裡有乙個抽象建模的過程。

抽象並不是物件導向語言專屬,其實它和語言無關,本質上是乙個思考的方式。它和分離的最大區別在於,抽象強調將細節隱藏,只關注核心的本質。而後者則重視於細節問題的分解和組合。

以求固定兩點的最快捷路線為例。從分離的角度來,可以分解為以下問題:

步行需要多少時間?

乘公共運輸多少時間?

乘的士多少時間?

組合以上答案,再評估哪乙個最快捷的方式。

而從抽象的角度來看,解決的思路會是這樣的:

遍歷所有可能的交通工具,取耗時最小的: 

1. 步行 

2. 乘公共運輸 

3. 乘的士

先給出乙個抽象的解決思路,至於細節,則是進一步的實現。抽象最大的威力在於它比實現要穩定,也最能用於固化核心設計。在開發過程中,常常圍繞著各種細節討論,似乎抽象過於虛。但是如果沒有以抽象來建立系統的設計全景,有些討論將變得效率低下。

在敏捷軟體開發:原則、模式與實踐中,martin大叔簡單的用抽象類在總類個數中的佔比作為抽象性的度量,再結合穩定性的度量,用來評估設計。詳情可以參考元件設計原則之概念篇(三)。

設計和實現時引入不必要的抽象或分解,也是一種複雜度.考慮擴充套件性也是確定會發生的需求才要考慮進來,否則就是引入不必要的複雜性.這也是敏捷設計所倡導的. 

一些約定俗成的命名,常常隱含著設計.比如observer, client, adapter等等.我們要學習這些模式,也要準確加以命名.否則很容易造成理解上的問題.

軟體設計是乙個平衡的過程,軟體的複雜度決定著系統的可維護性、可擴充套件性和靈活性。我們再來回顧一下前人定義出軟體設計的三原則:模組化、抽象和資訊隱藏。mccabe也曾有**專門討論將圈複雜度應用度量設計的複雜度,不過已經歷史久遠。現在來看以依賴關係來評估設計的複雜度會更為有效。有興趣可以了解一下cppdepend。另外google的工程師則基於llvm ir也實現了乙個工具用於依賴關係分析(generateing precise dependencies for large software)。

軟體設計的複雜度

軟體技術發展的使命之一就是控制複雜度 complexity 從高階語言的產生,到結構化程式設計,再到物件導向程式設計 元件化程式設計等等。關於複雜度的定義並不一致,想要詳細了解的可以讀讀the many faces of complexity in software design.英文中comple...

軟體設計的複雜度

軟體技術發展的使命之一就是控制複雜度 complexity 從高階語言的產生,到結構化程式設計,再到物件導向程式設計 元件化程式設計等等。關於複雜度的定義並不一致,想要詳細了解的可以讀讀the many faces of complexity in software design.英文中comple...

軟體複雜度評價

在應用軟體和嵌入式產品 包括硬體 嵌入式軟體 的可靠性設計中,有一條基本原則就是 簡單可靠 分析軟體複雜度,可以支援對軟體的質量檢查 應用分析 以及評估後續維護成本,方便查明那些可能導致錯誤的 比如開展圈複雜度分析技術,對軟體進行結構測試。以軟體複雜度測量的數目為基礎,幫助工程師識別難於測試和維護的...