10年碼農血與淚:重新撿起軟體設計原則

回歸最本質的,追求最基礎的!發現軟體世界那些不變的真理!

什麼是原則?

原則是指經過長期經驗總結所得出的合理化的現象。 ——百度百科

原則具有指導性、適用性、異樣性

指導性:在猶豫要不要做某件事情的時候,可以根據自身的原則出發作出抉擇,所以 瑞·達利歐 根據自己的行為標出了一本書《原則》。

適用性:人在社會群體中分工合作實現自我生存和推動社會發展,在這個漫長的過程中,逐漸形成了一些人們對於言行的共識,例如欠債還錢,殺人償命等。

異樣性:每個人對某件事或某個觀點、態度的原則不同。比如股民會根據自己對股票的理解、市場的觀察以及自身的認知,產生不同的操作原則。

為什麼要學軟體設計原則

軟體設計原則沒有銀彈,均是在代碼開發過程中總結出來的規律,而不是自然存在的真理。

學習設計原則,就像學習交通規則一樣。如果編碼比喻為開車,那設計原則就相當於交通規則。要想讓自己的軟體在市場上生存,就需要讓自己的車在道路上遵守交通規則。

通過交通原則的學習,才能靈活地完成剎車、停車、轉向燈、變道等各種各樣複雜的社會場景。同樣的,通過軟體設計原則的學習,才能靈活地設計出抽象、聚合、耦合、健壯的代碼來滿足各種各樣的業務場景。

如果用一句話總結:寫出優雅的、健壯的、可運行的程序!

所以在學習設計原則和編碼的時候,我們就應該多想一點:這樣寫夠不夠優雅?這樣寫夠不夠健壯?如果答案是肯定的,那就已經是最好的設計了。

軟體設計原則有哪些?

面向對象設計5個基本原則

2009年的時候,一個叫羅伯特·C·馬丁的哥們在一次大會上提出了SOLID的概念,即:

單一職責原則(Single responsibility principle,SRP)、

開閉原則 (The Open/Closed Principle, OCP) 、

里氏替換原則(Liskov Substitution principle)、

介面隔離原則(Interface-segregation principles,ISP)、

依賴反轉原則(Dependency inversion principle,DIP)。

注意只是這哥們提出的SOLID的概念,而不是這哥們創造的這5個原則。這些原則在之前已經存在,而他是將這些原則整合了起來。就像喬布斯只是將手機、MP3和相機整合起來,做出了IPhone,而不是喬布斯創造了智能手機。

這裡多提一下羅伯特·C·馬丁,這哥們是一位世界級軟體開發大師、設計模式和敏捷開發先驅。他在2010年出版了《代碼整潔之道》一書,這本書一經面世,就在軟體開發行業掀起了軒然大波 。小編最近還重刷了一遍,12年前的規範在計算機世界依然適用而不落俗套,是多麼的難能可貴。

再看一下這哥們出版的圖書,在豆瓣上的評分:

可以看到他出品的圖書的評分都相當的高,足以見得其實力非凡!

單一職責原則(Single responsibility principle,SRP)

這個原則是由馬丁先生在他的《敏捷軟體開發,原則,模式和實踐》一書中給出的。不過也是建立在巨人的肩膀之上產生的,說白了就是我們軟體工程師常說的:高內聚!這裡最抽象的是什麼是職責?馬丁先生的定義是"改變的原因"。

例子:

可以想像一下我們現實生活中製作一個報表。我們改變它有兩個原因:

1、報表內容的可以編輯,比如修改、格式化等功能

2、報表格式可以改變,比如轉為PDF、列印等功能

單一功能原則認為這兩方面的問題事實上是兩個分離的功能。這樣我們在設計的時候,應該設計到兩個不同的類或模塊里,從而做到每個類或模塊只有一個功能。

這樣做有什麼好處呢?

想像一下,如果不分開的話。我們修改了報表的內容,我們會擔心列印功能可能會不好使嗎?同樣我們列印了報表,會擔心報表的內容不正確嗎? 顯然不會,那就是因為我們的編輯部影響列印,我們的列印也不影響編輯,從而可以讓軟體更健壯。

開閉原則 (The Open/Closed Principle, OCP)

這個原則是伯特蘭·邁耶在他1988年發行的《面向對象軟體構造》中給出。馬丁先生1996年發表的文章《開閉原則》是使用這種方法的啟發式著作。 這個原則的核心是:

一旦完成了,除了錯誤的情況下再修改,否則都通過拓展的方式完成新的功能。

簡單地說就是:

對拓展開放,對修改關閉。

例子:

我們要發送通知。比如現在有了郵件通知,現在要添加電話通知。我們如果在原有的通知上修改的話,我們就可能會影響到之前的郵件通知,這樣就帶來了風險。 所以我們不在原來的通知上修改,而是拓展新的通知方式,有需要的可以調用新的通知方式。這樣就規避了原有業務的風險。

但是我們如何能夠對接上新的通知呢?這就要求很高的抽象能力。比如郵件通知需要郵箱號碼,電話通知需要電話號碼,甚至通知的形式和內容也不一樣。 如果上層做了很好的抽象,通知的發送邏輯各自實現,可以做到很優雅自如的表現。就像我們無論是坐火車、還是坐飛機,我們只需要人過去就可以乘坐一樣。

里氏替換原則(Liskov Substitution principle)

它由芭芭拉·利斯科夫Barbara Liskov)在1987年在一次會議上名為「數據的抽象與層次」的演說中首先提出。所以被命名為Liskov Substitution principle。

所謂的里氏替換,本質上具體能代替抽象,兒子擁有父親的所有能力(父親的錢兒子能花)。

例子:

一個工廠招工,那這時候男工、女工都可以參與;如果一個工廠招女工,那這時候男工就不能參與,而女工就可以參與;如果一個工廠招青年女工,那這時候年齡大的女工不可參與等等。

那為什麼要這樣設計呢?這就涉及到計算機軟體需要遵守的另一個特性:健壯性!在實際項目中,每個子類對應不同的業務含義,使用父類作為參數,傳遞不同的子類從而完成不同的業務邏輯。

介面隔離原則(Interface-segregation principles,ISP)

我們每個人都喜歡簡潔,我需要的你給我,我不需要的你帶走。同樣在程序的世界裡也是一樣的。如果做到這種簡潔呢?就需要介面隔離。

例子:

我只想走路,你就給我一雙鞋就行了;我想騎車,你就只給我自行車就夠了。如果我既想走路,又想騎車,那就給我一雙鞋和一輛自行車。

這樣我們就做到了,不想要的無需關注,從而達到了一種隔離的效果。進而讓軟體之間解耦

介面隔離原則(ISP)的目的是系統解開耦合,從而容易重構,更改和部署。

依賴反轉原則(Dependency inversion principle,DIP)

依賴反轉原則由羅伯特·C·馬丁提出,並且在數篇公開著作中被表述,包括論文《面向對象設計質量標準:對於依賴的分析》,以及一篇1996年出現在C++報道中的名為《依賴反轉原則》的文章,和《敏捷軟體開發,原則,模式和實踐》,《C#中的敏捷原則,模式和實踐》兩本書。

例子:父親需要依賴兒子的某項能力,但理論上父親不能直接調度兒子(比如:兒子還沒出生或兒子還未成年),那這時候父親將這個能力就行抽象化,等兒子長大了來把這個抽象具體實現了。這就是所謂的依賴反轉。

該原則規定:

高層次的模塊不應該依賴於低層次的模塊,兩者都應該依賴於抽象介面。 抽象介面不應該依賴於具體實現。而具體實現則應該依賴於抽象介面。

其他設計原則

迪米特法則 (Law of Demeter,LoD)

迪米特法則 (Law of Demeter,縮寫LoD)亦被稱作「最少知識原則(Principle of Least Knowledge)。這個原理的名稱來源於希臘神話中的農業女神,孤獨的迪米特。該原則是美國東北大學在1987年末在發明的,可以簡單地以下面任一種方式總結:

每個單元對於其他的單元只能擁有有限的知識:

只是與當前單元緊密聯繫的單元; 每個單元只能和它的朋友交談:

不能和陌生單元交談;

只和自己直接的朋友交談。

總之如果用現代的話就是:專業的人做專業的事,集中注意力辦大事!

例子:

人可以命令一條狗行走,但是不應該直接指揮狗的腿行走,應該由狗去指揮控制它的腿如何行走。

迪米特法則使得軟體更好的可維護性與適應性。因為對象較少依賴其它對象的內部結構,可以改變對象自身的實現而不用改變它的調用者。

不要重複自己(Don't repeat yourself,DRY)

DRY(干)的原則是「系統中的每一部分,都必須有一個單一的、明確的、權威的代表」。其反例為WET(濕),其有多種全稱,包括「Write everything twice」(把每個東西寫兩次)、「We enjoy typing」(我們就是喜歡打字)或「Waste everyone's time」(浪費大家的時間)。

例子:

比如人用腿走路,這時候我們如果有多個地都寫了人用腿走路,如果突然人進化了,還可以用手走路。這時候就需要改多處,才能實現。從而增加了工作量和不可控因素。

但是有時候為了可讀性,或避免耦合,或過早重構,或開發效率,應放棄DRY原則。

設計原則是一種規範,或是一種指導。真實的場景不見得必須要使用過設計原則。就像寫一首五言律詩,在不遵守原則的情況下能寫出沁人心脾、攝人心魄的詩也是值得敬仰的。

結語

除了以上提到的原則,還有很多原則,比如包括:

KISS原則(Keep It Simple, Stupid):其本質是追求簡潔、易於維護,和單一職責原則類似。

YAGNI 原則(You Ain』t Gonna Need It):程序員應該在面臨確鑿的需求時,才實現相應功能,意在不要過度設計。

原則可能多種多樣,其目的只有一個:寫出高質量的代碼!

在實際工作中,不要迷信原則,畢竟原則也是為了讓工作更輕鬆,如果一味迷信成為負擔了,那就本末倒置、得不償失了。