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


通過組合/聚合復用的優(yōu)缺點
優(yōu)點:
- 新對象存取子對象的唯一方法是通過子對象的接口。
- 這種復用是黑箱復用,因為子對象的內(nèi)部細節(jié)是新對象所看不見的。
- 這種復用更好地支持封裝性。
- 這種復用實現(xiàn)上的相互依賴性比較小。
- 每一個新的類可以將焦點集中在一個任務(wù)上。
- 這種復用可以在運行時間內(nèi)動態(tài)進行,新對象可以動態(tài)的引用與子對象類型相同的對象。
- 作為復用手段可以應(yīng)用到幾乎任何環(huán)境中去。
缺點:
- 系統(tǒng)中會有較多的對象需要管理。
通過繼承來進行復用的優(yōu)缺點
優(yōu)點:
- 新的實現(xiàn)較為容易,因為基類的大部分功能可以通過繼承的關(guān)系自動進入派生類。
- 修改和擴展繼承而來的實現(xiàn)較為容易。
缺點:
- 繼承復用破壞封裝性,因為繼承將基類的實現(xiàn)細節(jié)暴露給派生類。由于基類的內(nèi)部細節(jié)常常是對于派生類透明的,所以這種復用是透明的復用,又稱“白箱”復用。
- 如果基類發(fā)生改變,那么派生類的實現(xiàn)也不得不發(fā)生改變。
- 從基類繼承而來的實現(xiàn)是靜態(tài)的,不可能在運行時間內(nèi)發(fā)生改變,沒有足夠的靈活性。