做者:袁英傑
原文地址:https://codingstyle.cn/topics/185ide
咱們一直在談簡單設計,但究竟什麼是簡單設計?更具體的說,對於同一個問題,設計決策A和B,究竟哪個更符合簡單設計的要求?函數
對於這類問題,若是沒有一個明確的標尺,那麼"簡單設計"就難免會成爲一句沒法評判的空洞口號,讓程序設計者無從判斷和遵照。測試
對此,Kent Beck給出了清晰的答案:ui
經過全部測試(Passes its tests)設計
儘量消除重複 (Minimizes duplication)code
儘量清晰表達 (Maximizes clarity)排序
更少代碼元素 (Has fewer elements)接口
以上四個原則的重要程度依次下降。ip
這組定義被稱作簡單設計原則。ci
初看上去,這組原則平淡無奇,彷佛是一組耳熟能詳的原則的羅列。但只要細細品味,就會發現其精妙絕倫之處。
直觀的看,這句話貌似在講測試:一個項目只有具有完善的自動化測試,纔算在作簡單設計。
但事實上並不是如此。這裏提到的測試,真正的意思是客戶驗收。若是你的項目經過了客戶的全部驗收條件(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 Aren't Gonna Need It)。它強調,咱們要着眼當下,不去爲本身猜測出的將來可能性去增長系統的複雜度。
總之,當你看到一個代碼元素,沒有產生以前三條中的任何一條價值,那麼它就應該被刪除掉。
而這正是簡單設計可以簡單的緣由。
拋開第四條,單看前三條,它們以前的重要程度也是依次下降的。好比,你不該該由於怕產生很難消除、或乾脆消除不掉的重複而放棄一個對客戶有價值的需求。換句話說,哪怕一個需求會致使重複代碼,你也要去實現它。
一樣的,若是一個需求,會致使你的設計更加晦澀,你卻不該該由於它損害了清晰性、可理解性而拒絕它。
對於這兩點,絕大多數人都沒有太多爭議。(也有一派認爲,不該該讓需求破壞設計的優雅,當二者發生衝突時,選擇優雅)。
咱們常常可以聽到一種爭議:一些人認爲,放在一塊兒的長篇累牘的大塊流程代碼,反而更容易理解。由於消除重複而致使的單一職責,會將一大段代碼分佈到不一樣的類或者模塊,爲了理解它,就不得不在類或者模塊間跳來跳去,反而不容理解了。
另一部分人並不認同這種見解。他們認爲,因爲模塊或類的單一職責性,其每塊邏輯都更加簡單清晰,而後在另一個層面再去看它們之間的交互,就能夠很快理解整個邏輯。 這比大坨的麪條式代碼更容易理解。而對於SLAP(Single Level of Abstraction Priciple)的遵照,會更進一步的增長可理解性。
因爲可理解性屬於更加我的、更加主觀的事情,於是究竟哪一種方式更容易理解,可能永遠也不會有統一的答案。
而簡單設計原則經過第二條和第三條之間的排序,給出了清晰的決策依據:因爲消除重複,把一大塊代碼分隔到了不一樣的地方,即使團隊認爲這確實損害了可理解性,但因爲重複所致使的惡果更加嚴重,於是優先選擇消除重複。
另外,關於簡單設計原則,社區內有多個版本,用詞不一樣,但意思大體相同。關鍵的差異是第二條和第三條的順序:即消除重複和提高表達力哪一個更重要。有一些人認爲表達力比消除重複重要,於是把提高表達力放在第二條。但更多人認同的是本文以前的版本。
對於這一點,我我的的觀點是,越是主觀的東西,就越不具有可驗證性或科學性,於是對於工程技術而言,重要程度就越低。
另外,迴歸到具體項目裏,爲了不爭議,在不違背前兩點原則的狀況下,團隊能夠根據大多數人的審美和認知,決定怎樣的設計才更具有可理解性。事實上,在重複已經被消除殆盡的狀況下,對於可理解性問題,不管怎樣選擇,影響都是局部的。
於是,對於這個問題,團隊以爲舒服最重要。
簡單設計原則,經過對需求、易修改性、可理解性、複雜度,這四個在設計決策中最關鍵的因素給出了排序,讓簡單設計再也不一個語義模糊的口號,而是對設計決策給出了清晰的guideline。
根據筆者經驗,深刻理解、並在項目中反覆品味和應用它,能夠避免掉不少沒必要要的爭議,也會對設計質量產生很是顯著的幫助。