設計模式之禪(一)——六大設計原則

CSDN博客地址:https://blog.csdn.net/wf96390/article/details/89601820

六大設計原則

單一職責原則

英文名稱是Single Responsibility Principle,簡稱是SRP。單一職責原則的定義是:應該有且僅有一個原因引起類的變更。
這個原則存在爭議之處在哪里呢?就是對職責的定義,什么是類的職責,以及怎么劃分類的職責。我以前的設計是不是有點問題了?不,不是的,不要懷疑自己的技術能力,單一職責原則最難劃分的就是職責。一個職責一個接口,但問題是“職責”沒有一個量化的標準,一個類到底要負責那些職責?這些職責該怎么細化?細化后是否都要有一個接口或類?這些都需要從實際的項目去考慮,從功能上來說。
單一職責原則有什么好處:

  • 類的復雜性降低,實現(xiàn)什么職責都有清晰明確的定義;
  • 可讀性提高,復雜性降低,那當然可讀性提高了;
  • 可維護性提高,可讀性提高,那當然更容易維護了;
  • 變更引起的風險降低,變更是必不可少的,如果接口的單一職責做得好,一個接口修改只對相應的實現(xiàn)類有影響,對其他的接口無影響,這對系統(tǒng)的擴展性、維護性都有非常大的幫助。

里氏替換原則

在面向對象的語言中,繼承是必不可少的、非常優(yōu)秀的語言機制,它有如下優(yōu)點:

  • 代碼共享,減少創(chuàng)建類的工作量,每個子類都擁有父類的方法和屬性;
  • 提高代碼的重用性;
  • 子類可以形似父類,但又異于父類,“龍生龍,鳳生鳳,老鼠生來會打洞”是說子擁有父的“種”,“世界上沒有兩片完全相同的葉子”是指明子與父的不同;
  • 提高代碼的可擴展性,實現(xiàn)父類的方法就可以“為所欲為”了,君不見很多開源框架的擴展接口都是通過繼承父類來完成的;
  • 提高產(chǎn)品或項目的開放性。

自然界的所有事物都是優(yōu)點和缺點并存的,即使是雞蛋,有時候也能挑出骨頭來,繼承的缺點如下:

  • 繼承是侵入性的。只要繼承,就必須擁有父類的所有屬性和方法;
  • 降低代碼的靈活性。子類必須擁有父類的屬性和方法,讓子類自由的世界中多了些約束;
  • 增強了耦合性。當父類的常量、變量和方法被修改時,必需要考慮子類的修改,而且在缺乏規(guī)范的環(huán)境下,這種修改可能帶來非常糟糕的結果—大片的代碼需要重構。
    Java使用extends關鍵字來實現(xiàn)繼承,它采用了單一繼承的規(guī)則,C++則采用了多重繼承的規(guī)則,一個子類可以繼承多個父類。從整體上來看,利大于弊,怎么才能讓“利”的因素發(fā)揮最大的作用,同時減少“弊”帶來的麻煩呢?解決方案是引入里氏替換原則(Liskov Substitution Principle,LSP),什么是里氏替換原則呢?它有兩種定義:
  • 第一種定義,也是最正宗的定義:If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T,the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T.(如果對每一個類型為S的對象o1,都有類型為T的對象o2,使得以T定義的所有程序P在所有的對象o1都代換成o2時,程序P的行為沒有發(fā)生變化,那么類型S是類型T的子類型。)
  • 第二種定義:Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.(所有引用基類的地方必須能透明地使用其子類的對象。)
    第二個定義是最清晰明確的,通俗點講,只要父類能出現(xiàn)的地方子類就可以出現(xiàn),而且替換為子類也不會產(chǎn)生任何錯誤或異常,使用者可能根本就不需要知道是父類還是子類。但是,反過來就不行了,有子類出現(xiàn)的地方,父類未必就能適應。

里氏替換原則為良好的繼承定義了一個規(guī)范,一句簡單的定義包含了4層含義。

  1. 子類必須完全實現(xiàn)父類的方法
  2. 子類可以有自己的個性
  3. 覆蓋或實現(xiàn)父類的方法時輸入?yún)?shù)可以被放大
  4. 覆寫或實現(xiàn)父類的方法時輸出結果可以被縮小
    父類的一個方法的返回值是一個類型T,子類的相同方法(重載或覆寫)的返回值為S,那么里氏替換原則就要求S必須小于等于T,也就是說,要么S和T是同一個類型,要么S是T的子類。

依賴倒置原則

依賴倒置原則(Dependence Inversion Principle,DIP)的原始定義是:
High level modules should not depend upon low level modules.Both should depend upon abstractions.Abstractions should not depend upon details.Details should depend upon abstractions.
翻譯過來,包含三層含義:

  • 高層模塊不應該依賴低層模塊,兩者都應該依賴其抽象;
  • 抽象不應該依賴細節(jié);
  • 細節(jié)應該依賴抽象。
    高層模塊和低層模塊容易理解,每一個邏輯的實現(xiàn)都是由原子邏輯組成的,不可分割的原子邏輯就是低層模塊,原子邏輯的再組裝就是高層模塊。那什么是抽象?什么又是細節(jié)呢?在Java語言中,抽象就是指接口或抽象類,兩者都是不能直接被實例化的;細節(jié)就是實現(xiàn)類,實現(xiàn)接口或繼承抽象類而產(chǎn)生的類就是細節(jié),其特點就是可以直接被實例化,也就是可以加上一個關鍵字new產(chǎn)生一個對象。依賴倒置原則在Java語言中的表現(xiàn)就是:
  • 模塊間的依賴通過抽象發(fā)生,實現(xiàn)類之間不發(fā)生直接的依賴關系,其依賴關系是通過接口或抽象類產(chǎn)生的;
  • 接口或抽象類不依賴于實現(xiàn)類;
  • 實現(xiàn)類依賴接口或抽象類。
    更加精簡的定義就是“面向接口編程”—OOD(Object-Oriented Design,面向對象設計)的精髓之一。

對象的依賴關系有三種方式來傳遞,如下所示。

  1. 構造函數(shù)傳遞依賴對象
  2. Setter方法傳遞依賴對象
  3. 接口聲明依賴對象

只要遵循以下的幾個規(guī)則就可以:

  • 每個類盡量都有接口或抽象類,或者抽象類和接口兩者都具備
    這是依賴倒置的基本要求,接口和抽象類都是屬于抽象的,有了抽象才可能依賴倒置。
  • 變量的表面類型盡量是接口或者是抽象類
    很多書上說變量的類型一定要是接口或者是抽象類,這個有點絕對化了,比如一個工具類,xxxUtils一般是不需要接口或是抽象類的。還有,如果你要使用類的clone方法,就必須使用實現(xiàn)類,這個是JDK提供的一個規(guī)范。
  • 任何類都不應該從具體類派生
    如果一個項目處于開發(fā)狀態(tài),確實不應該有從具體類派生出子類的情況,但這也不是絕對的,因為人都是會犯錯誤的,有時設計缺陷是在所難免的,因此只要不超過兩層的繼承都是可以忍受的。特別是負責項目維護的同志,基本上可以不考慮這個規(guī)則,為什么?維護工作基本上都是進行擴展開發(fā),修復行為,通過一個繼承關系,覆寫一個方法就可以修正一個很大的Bug,何必去繼承最高的基類呢?(當然這種情況盡量發(fā)生在不甚了解父類或者無法獲得父類代碼的情況下。)
  • 盡量不要覆寫基類的方法
    如果基類是一個抽象類,而且這個方法已經(jīng)實現(xiàn)了,子類盡量不要覆寫。類間依賴的是抽象,覆寫了抽象方法,對依賴的穩(wěn)定性會產(chǎn)生一定的影響。
  • 結合里氏替換原則使用
    接口負責定義public屬性和方法,并且聲明與其他對象的依賴關系,抽象類負責公共構造部分的實現(xiàn),實現(xiàn)類準確的實現(xiàn)業(yè)務邏輯,同時在適當?shù)臅r候對父類進行細化。

接口隔離原則

接口分為兩種:

  • 實例接口(Object Interface),在Java中聲明一個類,然后用new關鍵字產(chǎn)生一個實例,它是對一個類型的事物的描述,這是一種接口。比如你定義Person這個類,然后使用Person zhangSan=new Person()產(chǎn)生了一個實例,這個實例要遵從的標準就是Person這個類,Person類就是zhangSan的接口。疑惑?看不懂?不要緊,那是因為讓Java語言浸染的時間太長了,只要知道從這個角度來看,Java中的類也是一種接口。
  • 類接口(Class Interface),Java中經(jīng)常使用的interface關鍵字定義的接口。
    主角已經(jīng)定義清楚了,那什么是隔離呢?它有兩種定義,如下所示:
  • Clients should not be forced to depend upon interfaces that they don’t use.(客戶端不應該依賴它不需要的接口。)
  • The dependency of one class to another one should depend on the smallest possible interface.(類間的依賴關系應該建立在最小的接口上。)

接口隔離原則是對接口進行規(guī)范約束,其包含以下4層含義:

  • 接口要盡量小
    這是接口隔離原則的核心定義,不出現(xiàn)臃腫的接口(Fat Interface),但是“小”是有限度的,首先就是不能違反單一職責原則。
  • 接口要高內(nèi)聚
    高內(nèi)聚就是提高接口、類、模塊的處理能力,減少對外的交互。
  • 定制服務
    定制服務就是單獨為一個個體提供優(yōu)良的服務。我們在做系統(tǒng)設計時也需要考慮對系統(tǒng)之間或模塊之間的接口采用定制服務。采用定制服務就必然有一個要求:只提供訪問者需要的方法。
  • 接口設計是有限度的
    接口的設計粒度越小,系統(tǒng)越靈活,這是不爭的事實。但是,靈活的同時也帶來了結構的復雜化,開發(fā)難度增加,可維護性降低,這不是一個項目或產(chǎn)品所期望看到的,所以接口設計一定要注意適度,這個“度”如何來判斷呢?根據(jù)經(jīng)驗和常識判斷,沒有一個固化或可測量的標準。

在實踐中可以根據(jù)以下幾個規(guī)則來衡量:

  • 一個接口只服務于一個子模塊或業(yè)務邏輯;
  • 通過業(yè)務邏輯壓縮接口中的public方法,接口時常去回顧,盡量讓接口達到“滿身筋骨肉”,而不是“肥嘟嘟”的一大堆方法;
  • 已經(jīng)被污染了的接口,盡量去修改,若變更的風險較大,則采用適配器模式進行轉化處理;
  • 了解環(huán)境,拒絕盲從。每個項目或產(chǎn)品都有特定的環(huán)境因素,別看到大師是這樣做的你就照抄。千萬別,環(huán)境不同,接口拆分的標準就不同。深入了解業(yè)務邏輯,最好的接口設計就出自你的手中!

迪米特法則

迪米特法則(Law of Demeter,LoD)也稱為最少知識原則(Least Knowledge Principle,LKP),雖然名字不同,但描述的是同一個規(guī)則:一個對象應該對其他對象有最少的了解。通俗地講,一個類應該對自己需要耦合或調(diào)用的類知道得最少。迪米特法則的核心觀念就是類間解耦,弱耦合,只有弱耦合了以后,類的復用率才可以提高。

迪米特法則對類的低耦合提出了明確的要求,其包含以下4層含義。

  1. 只和朋友交流
    迪米特法則還有一個英文解釋是:Only talk to your immedate friends(只與直接的朋友通信。)什么叫做直接的朋友呢?每個對象都必然會與其他對象有耦合關系,兩個對象之間的耦合就成為朋友關系,這種關系的類型有很多,例如組合、聚合、依賴等。
  2. 朋友間也是有距離的
    人和人之間是有距離的,太遠關系逐漸疏遠,最終形同陌路;太近就相互刺傷。迪米特法則就是對這個距離進行描述,即使是朋友類之間也不能無話不說,無所不知。一個類公開的public屬性或方法越多,修改時涉及的面也就越大,變更引起的風險擴散也就越大。迪米特法則要求類“羞澀”一點,盡量不要對外公布太多的public方法和非靜態(tài)的public變量。
  3. 是自己的就是自己的
    在實際應用中經(jīng)常會出現(xiàn)這樣一個方法:放在本類中也可以,放在其他類中也沒有錯,那怎么去衡量呢?你可以堅持這樣一個原則:如果一個方法放在本類中,即不增加類間關系,也對本類不產(chǎn)生負面影響,就放置在本類中。
  4. 謹慎使用Serializable

開閉原則

開閉原則的定義:
Software entities like classes,modules and functions should be open for extension but closed for modifications.(一個軟件實體如類、模塊和函數(shù)應該對擴展開放,對修改關閉。)

開閉原則是最基礎的一個原則,前五章節(jié)介紹的原則都是開閉原則的具體形態(tài),也就是說前五個原則就是指導設計的工具和方法,而開閉原則才是其精神領袖。
可通過以下幾個方面來理解其重要性。

  1. 開閉原則對測試的影響
  2. 開閉原則可以提高復用性
  3. 開閉原則可以提高可維護性
  4. 面向對象開發(fā)的要求

把這6個原則的首字母(里氏替換原則和迪米特法則的首字母重復,只取一個)聯(lián)合起來就是SOLID(solid,穩(wěn)定的),其代表的含義也就是把這6個原則結合使用的好處:建立穩(wěn)定、靈活、健壯的設計,而開閉原則又是重中之重,是最基礎的原則,是其他5大原則的精神領袖。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 設計模式六大原則 設計模式六大原則(1):單一職責原則 定義:不要存在多于一個導致類變更的原因。通俗的說,即一個類...
    viva158閱讀 826評論 0 1
  • 轉載標注聲明:http://www.uml.org.cn/sjms/201211023.asp 目錄:[設計模式六...
    Bloo_m閱讀 799評論 0 7
  • 轉載自 設計模式六大原則[http://www.uml.org.cn/sjms/201211023.asp#3] ...
    廚子閱讀 1,176評論 2 5
  • 設計模式六大原則(1):單一職責原則 定義:不要存在多于一個導致類變更的原因。通俗的說,即一個類只負責一項職責。 ...
    Jabir_Zhang閱讀 675評論 0 3
  • 我最愛的任性先生 我現(xiàn)在正單曲循環(huán)著你最喜歡的音樂 在這樣的深夜 寫下這封,我早該為你撰寫的情書 不知道此刻,你孤...
    一唱閱讀 611評論 0 1

友情鏈接更多精彩內(nèi)容