軟件設計的複雜度

什麼是軟件設計的複雜度

軟件技術發展的使命之一就是控制複雜度(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, 各種第三方庫越來越多,模塊間的依賴就會越來越複雜。
cycle_dependencies
顯然系統中的模塊或者組件太多了,需要進一步整理。但真正的問題在於出現了雙向和環形的依賴。比如上圖中負責計算的Computing模塊也依賴到了UI模塊,或許是因爲UI層持有一個計算所需的關鍵參數。如果UI層變更,就可能會影響到Computing,出現無法預測的行爲,給客戶以不穩定的印象。

所以模塊間的依賴關係必須簡化,絕對不能出現環形的依賴。以Chromium爲例,它對各個模塊的依賴就有嚴格的定義,並且有DEPS在編譯期保證程序員不會犯錯。下圖是Chromium Component依賴關係的定義,其中Component內部目錄的依賴關係也有定義:
chromium_component

當底層模塊需要依賴上層模塊的實現時,就要通過依賴倒置(DIP)來處理。簡單而言就是由底層模塊定義一個接口,要求上層模塊實現並注入到底層模塊。
DIP

使用抽象降低複雜度

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

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

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

  1. 步行需要多少時間?
  2. 乘公共交通多少時間?
  3. 乘的士多少時間?
  4. 組合以上答案,再評估哪一個最快捷的方式。

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

遍歷所有可能的交通工具,取耗時最小的:
1. 步行
2. 乘公共交通
3. 乘的士

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

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

以通俗原則降低複雜度

設計和實現時引入不必要的抽象或分解,也是一種複雜度.考慮擴展性也是確定會發生的需求才要考慮進來,否則就是引入不必要的複雜性.這也是敏捷設計所倡導的.
一些約定俗成的命名,常常隱含着設計.比如Observer, Client, Adapter等等.我們要學習這些模式,也要準確加以命名.否則很容易造成理解上的問題.

小結

軟件設計是一個平衡的過程,軟件的複雜度決定着系統的可維護性、可擴展性和靈活性。我們再來回顧一下前人定義出軟件設計的三原則:模塊化、抽象和信息隱藏。McCabe也曾有論文專門討論將圈複雜度應用度量設計的複雜度,不過已經歷史久遠。現在來看以依賴關係來評估設計的複雜度會更爲有效。有興趣可以瞭解一下CppDepend。另外Google的工程師則基於LLVM IR也實現了一個工具用於依賴關係分析(Generateing Precise Dependencies for Large Software)。

轉載請註明出處: http://blog.csdn.net/horkychen

發佈了220 篇原創文章 · 獲贊 29 · 訪問量 174萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章