標籤:

簡單設計原則

我們一直在談簡單設計,但究竟什麼是簡單設計?更具體的說,對於同一個問題,設計決策A和B,究竟哪一個更符合簡單設計的要求?

對於這類問題,如果沒有一個明確的標尺,那麼簡單設計就不免會成為一句無法評判的空洞口號,讓程序設計者無從判斷和遵守。

對此,Kent Beck給出了清晰的答案:

  1. 通過所有測試(Passes its tests)

  2. 儘可能消除重複 (Minimizes duplication)

  3. 儘可能清晰表達 (Maximizes clarity)

  4. 更少代碼元素 (Has fewer elements)

  5. 以上四個原則的重要程度依次降低

這組定義被稱做簡單設計原則

初看上去,這組原則平淡無奇,似乎是一組耳熟能詳的原則的羅列。但只要細細品味,就會發現其精妙絕倫之處。

通過所有測試

直觀的看,這句話貌似在講測試:一個項目只有具備完善的自動化測試,才算在做簡單設計

但事實上並非如此。這裡提到的測試,真正的意思是**客戶驗收**。如果你的項目通過了客戶的所有驗收條件(Acceptance Criteria),那就說明你們已經完成與客戶約定的全部需求。至於驗收方式是靠人工還是靠自動化測試則無關緊要。

所以,這句話強調的是對外部需求——包括功能性需求和非功能性需求——正確的完成。

儘可能消除重複

重複,意味著低內聚,高耦合。而消除重複的過程,也就意味著是讓軟體走向高內聚,低耦合,達到良好正交性的過程。識別和消除重複,對於增強軟體應對變化能力的重要程度,怎麼強調都不為過。關於這一點,我會另文說明,這裡就不再贅述。

不過,並不是所有的重複都可以消除:比如,C++一個源文件里對外部公開的類,其每個public方法原型,除了在源文件里定義時,需要聲明一次,還需要在頭文件里再次聲明。這樣的重複,是語言機制的要求,無法消除。

因而,這條原則被描述為最小化重複,而不是消除重複

儘可能清晰表達

清晰性,指的是一個設計容易理解的程度。注意:這不僅僅是對整潔代碼(Clean Code)及聲明式設計(Declarative Design)的強調。關於這一點,有著非常有趣的部分,我們在隨後的部分談到。

更少代碼元素

這一條是點睛之筆,正是因為它的存在,這組原則才被稱做簡單設計原則,從而區別於其它設計原則。

在這裡,常量,變數,函數,類,包 …… 都屬於代碼元素。代碼元素的數量,通常反映了設計的複雜度。因而,這句話強調的是:儘可能降低複雜度,保持簡單。

重要程度排序

這一句最容易讓人忽視,卻恰恰最為重要。如果第四條是點睛之筆,那麼第五條就是將之前四條貫穿起來的那條龍。正是這一句,讓你知道當以上四條發生衝突時,應該如何取捨。

我們已經知道,第四條,是簡單設計的精髓,但是,它在前四條原則卻最不重要。

對於第一條,它強調:簡單固然好,但你不能為了簡單,而不去實現和客戶約定好的需求。(當然,如果需求不合理,你應該在前期通過和客戶協商拒絕,或修改。但那是關於需求管理這個話題有關的故事,感興趣者可以去查閱相關文章和書籍)。

而對於第二條,比如,我們現在有兩個類:它們之間有一部分重複代碼。為了消除掉這個重複,我們將重複代碼提取到一個新的類里。於是兩個類變為三個類,增加了一個新的代碼元素。這當然讓設計變得更複雜了。但這種複雜度產生了更重要的價值(讓軟體更具備正交性,從而讓軟體更易於修改),所以,不能為了保持簡單,而不去消除這個重複。

對於第三條也是如此,比如下面這句代碼中有一個magic number:

a = 1000;

為了讓這段代碼更容易理解,我們將代碼修改為:

const int MAX_NUM_OF_CONNECTIONS = 1000;a = MAX_NUM_OF_CONNECTIONS;

從而增加了一個新的代碼元素。因而也稍微增加了設計的複雜度。但由於這個新的代碼元素也產生了相對於簡單更重要的價值,在簡單和表達力之間,我們應該選擇後者。

反向價值

而簡單設計的價值,也可以從相反的角度來看:如果新增的一條代碼元素,不能產生上述三個價值,它就不應該存在。

對於第一條,你不應該去實現一個客戶還不需要的需求,因為那會增加系統的複雜度。

對於第二條,你不應該為還沒有出現的重複,或者為尚未出現的變化方向,去增加任何額外的複雜度。比如建立一個抽象介面,卻只有一個對應的實現。

而第四條對於前兩條的約束,就是我們耳熟能祥的YAGNI(You Arent Gonna Need It)。它強調,我們要著眼當下,不去為自己猜想出的未來可能性去增加系統的複雜度。

總之,當你看到一個代碼元素,沒有產生之前三條中的任何一條價值,那麼它就應該被刪除掉。

而這正是簡單設計能夠簡單的原因。

需求最大

拋開第四條,單看前三條,它們之前的重要程度也是依次降低的。比如,你不應該因為怕產生很難消除、或乾脆消除不掉的重複而放棄一個對客戶有價值的需求。換句話說,哪怕一個需求會導致重複代碼,你也要去實現它。

同樣的,如果一個需求,會導致你的設計更加晦澀,你卻不應該因為它損害了清晰性、可理解性而拒絕它。

對於這兩點,絕大多數人都沒有太多爭議。(也有一派認為,不應該讓需求破壞設計的優雅,當兩者發生衝突時,選擇優雅)。

最具爭議話題的解決

我們經常能夠聽到一種爭議:一些人認為,放在一起的長篇累牘的大塊流程代碼,反而更容易理解。因為消除重複而導致的單一職責,會將一大段代碼分布到不同的類或者模塊,為了理解它,就不得不在類或者模塊間跳來跳去,反而不容理解了。

另外一部分人並不認同這種看法。他們認為,由於模塊或類的單一職責性,其每塊邏輯都更加簡單清晰,然後在另外一個層面再去看它們之間的交互,就可以很快理解整個邏輯。 這比大坨的麵條式代碼更容易理解。而對於SLAP(Single Level of Abstraction Priciple)的遵守,會更進一步的增加可理解性。

由於可理解性屬於更加個人、更加主觀的事情,因而究竟哪種方式更容易理解,可能永遠也不會有統一的答案。

而簡單設計原則通過第二條和第三條之間的排序,給出了清晰的決策依據:由於消除重複,把一大塊代碼分隔到了不同的地方,即便團隊認為這確實損害了可理解性,但由於重複所導致的惡果更加嚴重,因而優先選擇消除重複。

另外,關於簡單設計原則,社區內有多個版本,用詞不同,但意思大致相同。關鍵的差別是第二條和第三條的順序:即消除重複提升表達力哪個更重要。有一些人認為表達力比消除重複重要,因而把提升表達力放在第二條。但更多人認同的是本文之前的版本。

對於這一點,我個人的觀點是,越是主觀的東西,就越不具備可驗證性或科學性,因而對於工程技術而言,重要程度就越低。

另外,回歸到具體項目里,為了避免爭議,在不違背前兩點原則的情況下,團隊可以根據大多數人的審美和認知,決定怎樣的設計才更具備可理解性。事實上,在重複已經被消除殆盡的情況下,對於可理解性問題,無論怎樣選擇,影響都是局部的。

因而,對於這個問題,團隊覺得舒服最重要。

結論

簡單設計原則,通過對需求、易修改性、可理解性、複雜度,這四個在設計決策中最關鍵的因素給出了排序,讓簡單設計不再一個語義模糊的口號,而是對設計決策給出了清晰的guideline。

根據筆者經驗,深入理解、並在項目中反覆品味和應用它,可以避免掉很多不必要的爭議,也會對設計質量產生非常顯著的幫助。


推薦閱讀:

Raindrop 書籤整理軟體收費版使用感受
系統設計師必備技能與工具有哪些?
微服務設計—事件驅動架構

TAG:軟體設計 |