設計是一項把需求轉換為編碼方案的活動。設計在軟件開發中是必定存在的,無論何種情況下,我們都需要把需求轉化為編碼方案。
設計是一個不斷完善的過程,設計的缺陷可能在設計之初無法被發現,直到設計完成時才被察覺到,這導致了再次設計的需要。注意,我們在首次設計時應該盡可能的避免缺陷的出現,而不是視而不見以求下次解決。
設計上的付出是必要的,在設計階段發現錯誤并改正比編碼后發現相同錯誤并改正的代價低的多,如果設計的錯誤拖延到維護階段,那就更加不堪設想。
設計存在優劣,必須明確這一點。敷衍的進行設計后果是很嚴重的(雖然偶爾也能更加快速的產生產品),糟糕設計的最大特點是無法響應需求變更。但也應該注意,優秀的設計有多種,糟糕的設計也有多種。
事物的本質屬性是一件事物必須具有的,如果不具有則就不在是該事物。事物的偶然屬性是不起解決性作用的屬性。例如,車總有輪子,沒有了輪子就不在是車,那么輪子就是本質屬性,但是輪子可能有 4 個,也可能有 3 個,無論多少個輪子依然是車,那輪子的數目就是偶然屬性。
本質復雜度源于需求,無論采取何種手段,何種技術都不會對本質復雜度造成任何影響,所以有人才會說,軟件開發本身就很復雜(無論使用何種技術開發都是復雜的)。我們需要管理好偶然復雜度(偶然性的復雜度),例如,需求是開發一個可用的計算器程序(圖形界面可有可無),這時,開發命令行的計算器就比開發 GUI 的計算器的偶然復雜度小,用 C++ 進行開發其偶然復雜度就比用 Dephi 高。敏捷開發和 Unix 中都強調 KISS,而敏捷開發更加強調應該使用簡單的技術和工具,這都是為了避免偶然復雜度過高帶來額外的成本開銷。
設計者應該真正理解偶然復雜度,如果設計出來的框架,在進行開發時需要開發人員關注于系統的每個細節,那么對于程序員來說偶然復雜度是高的,相比下,對于每個功能的開發,程序員只需要關心當前編寫的任務,那么偶然復雜度是低的(COM 的設計非常強調的一點是封裝,而不是復用)。
設計的基本的原則:
1)盡量降低偶然復雜度。如果可以簡單的解決問題,那為什么不這么做了?另外某些角度來說:
模塊化的系統(系統對“模塊”一詞有確切的定義),在一定程度上偶然復雜度較低,模塊化的系統讓程序員只需要關心當前開發的部分。
2)盡量保持高內聚,低耦合。高耦合的系統懼怕變動(動一處而牽動全身),變動帶來的問題通過關聯進行傳遞。高耦合的系統的關鍵點在于:系統組成部分的關聯過多,導致過多的相互影響,當關聯數量到一定程度,整個系統就會很不穩定。
具體來說:
<1> 集成的困難:高耦合的各個程序組成部分需要花費較多時間進行集成。
<2> 測試的困難:一個部分的問題通過與其他部分關聯傳遞到其他部分,那么意味著一個部分的變動可能導致整個系統的重新測試。
低耦合:在類這個層次上來說,除了底層的工具類(例如 STL)之外(工具類允許和系統中的大部分類發生關聯),應該盡可能的減少類之間的關聯。
3)可擴展。可擴展的一個標志是,無需改變系統底層,即可增加新的功能。
4)可移植性。可移植性非常特別(似乎違背低偶然復雜度原則),它不同于軟件的其他的特性,除非你保證你的項目永遠無需移植到其他的環境中去,否則,你應該注意這點,因為通常來說,時間越長,越難移植,甚至最后只能重寫,代價非常之高。
5)分層。分層的系統相對來說偶然復雜度較低,關聯較少。每層都有每層的工作,相互影響較小。
另外,類的粒度也比較重要,大粒度的類間關聯少,但是不可避免的就是類自身過于復雜。小粒度的類自身很簡單,易于開發和維護,但是類間關聯變多,通訊太頻繁。粒度過大和過小都會帶來較多的問題,應該根據經驗作合理設計。
在 C++ 中使用巨大的類無異于使用 C 語言進行編程,這意味著你拋棄了 C++ 強大的抽象能力。含有巨類的系統中的一個顯著的標志是:含有大量重復代碼或者相似代碼(這歸結于 C 語言不夠強大的抽象能力和項目在時間上的壓力)
最后要說的一句是:KISS 和敏捷給太多人以誤解,請仔細斟酌它們的含義(有幾個人懂得 KISS 的真正含義)。設計是不可避免的,好的設計是至關重要的。好的設計在一定程度上控制了軟件的成本,即使你的老板并不明白,但是你應該這么做。
author: killercat