面向?qū)ο笤O(shè)計的七大設(shè)計原則

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

軟件實體(模塊,類,方法等)應(yīng)該對擴展開放,對修改關(guān)閉。

實現(xiàn)方法:面向接口編程。

  • 把不變的部分抽象成不變的接口,這些不變的接口可以應(yīng)對未來的擴展。
  • 接口的最小功能設(shè)計原則。原有的接口可以應(yīng)對未來的擴展;不足的部分可以通過定義新的接口來實現(xiàn)。
  • 模塊之間的調(diào)用通過抽象接口進行,這樣即使實現(xiàn)層發(fā)生變化,也無需修改調(diào)用方的代碼。

二、里氏替換原則(Liskov Substitution Principle,LSP)

所有引用基類的地方必須能透明地使用其派生類的對象。

LSP對我們是否應(yīng)該使用繼承提供了判斷的依據(jù),不再是簡單地根據(jù)兩者之間是否有相同之處來說使用繼承。子類可以擴展父類的功能,但不能改變父類原有的功能:

  • 子類可以實現(xiàn)父類的抽象方法,但不能覆蓋父類的非抽象方法。
  • 子類中可以增加自己特有的方法。
  • 當子類重載父類方法時,輸入?yún)?shù)要比父類的更加寬松。
  • 當子類的方法實現(xiàn)父類的方法時,返回值要比父類更加嚴格。

三、迪米特原則(Law of Demeter, LoD)

又叫最少知道原則,只與你直接的朋友們通信,不要跟“陌生人”說話。

LoD可以降低類之間的耦合。

  • 一個軟件實體應(yīng)當盡可能少地與其他實體發(fā)生相互作用。
  • 每一個軟件單位對其他的單位都只有最少的知識,而且局限于那些與本單位密切相關(guān)的軟件單位。

朋友圈(方法可以調(diào)用的對象):


使用LoD應(yīng)該注意:

  • LoD容易產(chǎn)生大量中介類,會增加系統(tǒng)的復雜度。
  • 朋友間也是有距離的。盡量不要對外公布過多的public方法和非靜態(tài)變量,多使用private、protected。
  • 是自己的就是自己的。若一個方法放在本類中,既不增加類間關(guān)系,也對本類不產(chǎn)生負面影響,就放置在本類中。

四、單一職責原則(Single Responsibility Principle, SRP)

永遠不要讓一個類存在多個改變的理由。換句話說,只能讓一個類/接口/方法有且僅有一個職責。

多個職責的缺點:

  • 一方面,如果一個職責使用了外部類庫,則使用另一個職責的用戶也不得不包含這個未被使用的外部類庫。
  • 另一方面,修改一個職責,另外一個職責的用戶也將受到影響,不得不重新編譯和配置。

SRP的優(yōu)點:

  • 從職責方面為我們對類(接口)的抽象的顆粒度建立了判斷基準。
  • 降低了類的復雜度,提高了類的可讀性,提高系統(tǒng)的可維護性,降低變更引起的風險。

五、接口分隔原則(Interface Segregation Principle, ISP)

不能強迫用戶去依賴他們不使用的接口。

使用多個專門的接口比使用單一的總接口總要好。用戶不應(yīng)該因為不使用的接口的改變而改變。

  • ISP從對接口的使用上為我們對接口抽象的顆粒度建立了判斷基準。
  • 符合高內(nèi)聚低耦合的設(shè)計思想,使類具有很好的可讀性、可擴展性和可維護性。
  • 接口分隔要適度,避免產(chǎn)生大量的細小接口。

實現(xiàn)方法:

  • 一個類對一個類的依賴應(yīng)該建立在最小的接口上。
  • 建立單一接口,不要建立龐大臃腫的接口。
  • 盡量細化接口,接口中的方法盡量少。

單一職責原則和接口分隔原則的區(qū)別:

  • SRP強調(diào)的是職責,方法可以很多,針對程序中實現(xiàn)的細節(jié)。
  • ISP強調(diào)的是約束接口,針對抽象、整體框架。

六、依賴倒置原則(Dependency Inversion Principle, DIP)

A. 高層模塊不應(yīng)該依賴于低層模塊,二者都應(yīng)該依賴于抽象。
B. 抽象不應(yīng)該依賴于細節(jié),細節(jié)應(yīng)該依賴于抽象。
C. 針對接口編程,不要針對實現(xiàn)編程。
  • 依賴:模塊A使用/調(diào)用模塊B,稱為A依賴B。
  • 低層模塊:實現(xiàn)了一些基本操作的類。
  • 高層模塊:封裝了某些復雜的邏輯,并且依賴于低層模塊的類。

高層模塊依賴低層模塊設(shè)計的問題:


  • 當?shù)蛯幽K需要修改或替換時,高層模塊將受到影響。
  • 高層模塊很難可以復用。

用DIP來解決問題:


  • 抽象接口層:對低層模塊的抽象。
  • 高層模塊依賴接口層,低層模塊繼承或?qū)崿F(xiàn)抽象接口。
  • 類和類之間通過抽象接口層來建立關(guān)系。

DIP優(yōu)點:
可以減少類間的耦合性、提高系統(tǒng)穩(wěn)定性,提高代碼可讀性和可維護性,可降低修改程序所造成的風險。

七、組合/聚合復用原則(Composite/Aggregate Reuse Principle, CARP)

盡量使用對象組合/聚合,而不是繼承關(guān)系達到軟件復用的目的。

在面向?qū)ο笤O(shè)計中,有兩種基本的辦法可以實現(xiàn)復用:第一種是通過組合/聚合,第二種就是通過繼承。

什么時候才應(yīng)該使用繼承?只有當以下的條件全部被滿足時,才應(yīng)當使用繼承關(guān)系:

  1. 派生類是基類的一個特殊種類,而不是基類的一個角色。"Is-A"使用繼承,"Has-A"使用聚合。
  2. 永遠不會出現(xiàn)需要將派生類換成另外一個類的派生類的情況。如果不能肯定將來是否會變成另外一個派生類的話,就不要使用繼承。
  3. 派生類具有擴展基類的責任,而不是具有置換掉(override)或注銷掉(Nullify)基類的責任。如果一個派生類需要大量的置換掉基類的行為,那么這個類就不應(yīng)該是這個基類的派生類。
  4. 只有在分類學角度上有意義時,才可以使用繼承。

例:


通過組合/聚合復用的優(yōu)缺點
優(yōu)點:

  1. 新對象存取子對象的唯一方法是通過子對象的接口。
  2. 這種復用是黑箱復用,因為子對象的內(nèi)部細節(jié)是新對象所看不見的。
  3. 這種復用更好地支持封裝性。
  4. 這種復用實現(xiàn)上的相互依賴性比較小。
  5. 每一個新的類可以將焦點集中在一個任務(wù)上。
  6. 這種復用可以在運行時間內(nèi)動態(tài)進行,新對象可以動態(tài)的引用與子對象類型相同的對象。
  7. 作為復用手段可以應(yīng)用到幾乎任何環(huán)境中去。

缺點:

  1. 系統(tǒng)中會有較多的對象需要管理。

通過繼承來進行復用的優(yōu)缺點
優(yōu)點:

  1. 新的實現(xiàn)較為容易,因為基類的大部分功能可以通過繼承的關(guān)系自動進入派生類。
  2. 修改和擴展繼承而來的實現(xiàn)較為容易。

缺點:

  1. 繼承復用破壞封裝性,因為繼承將基類的實現(xiàn)細節(jié)暴露給派生類。由于基類的內(nèi)部細節(jié)常常是對于派生類透明的,所以這種復用是透明的復用,又稱“白箱”復用。
  2. 如果基類發(fā)生改變,那么派生類的實現(xiàn)也不得不發(fā)生改變。
  3. 從基類繼承而來的實現(xiàn)是靜態(tài)的,不可能在運行時間內(nèi)發(fā)生改變,沒有足夠的靈活性。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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