原文鏈接
單一職責原則 Single Responsibility Principle
定義:一個類或者一個接口,最好只負責一項職責。
問題由來:類T負責兩個不同的職責P1和P2。由於職責P1需要發生改變而需要修改T類,就有可能導致原來運行正常的職責P2功能發生故障。
解決方法:遵循單一職責原則。分別建立新的類來對應相應的職責;這樣就能避免修改類時影響到其他的職責;
當遇到職責擴散的時候,在邏輯足夠簡單的時候,才可以在代碼級別上面違反單一職責原則,只有類中方法數量足夠少,才可以在方法級別上違反單一職責原則;
優點:類的復雜性將會降低,可讀性將會大大提高,維護性也會提高。
里氏替換原則 Liskov Substitution Principle
在使用基類的地方可以任意使用其子類,能保證子類完美替換基類;這一種精神其實是對繼承機制約束規范的體現。在父類和子類的具體實現中,嚴格控制繼承層次中的關系特征,以保證用子類替換基類時,程序行為不發生問題,且能正常進行下去。
對於繼承來說,父類定義了一系列的規范和契約,雖然不強制所有的子類必須遵從,但是如果子類對這些非抽象方法任意修改,就會對整個繼承體系造成破環。
如果非要重寫父類的方法,比較通用的方法是:原來的父類和子類都繼承一個更加通俗的基類,原有的繼承關系去掉,采用依賴、聚合、組合等關系代替;
原則包含了一下四層含義:
* 子類可以實現父類的抽象方法,但不能覆蓋父類的非抽象方法;
* 子類可以增加自己特有的方法;
* 當子類的方法重載父類的方法時,方法的形參要比父類方法的輸入參數更佳寬松;
* 當子類的方法實現父類的抽象方法時,方法的返回值要比父類更加嚴格;
依賴倒置原則 Dependence Inversion Principle
定義:高層模塊不應該依賴低層模塊,二者都應該依賴其抽象;抽象不應該依賴細節;細節應該依賴抽象,其核心思想是依賴於抽象;
問題由來:類A直接依賴類B,假如要將類A改為依賴類C,則必須通過修改類A的代碼來完成;這種場景下,類A一般是高層模塊,負責復雜的業務邏輯;類B和類C是低層模塊,負責基本的原則操作;假如修改類A,會給程序帶來不必要的風險。
解決方案:將類A修改為依賴接口I,類B和類C各自實現接口I,類A通過接口I來間接與類B和類C發生聯系,則會降低修改類A的幾率;
在實際中,我們一般需要做到以下三點:
* 低層模塊盡量都要有抽象類或者接口,或者兩者都有;
* 變量的聲明類型盡量是抽象類或者接口;
* 使用繼承時遵循里氏替換原則;
接口隔離原則 Interface Segregation Principle
定義:客戶端不應該依賴它不需要的接口;一個類對另一個類的依賴應該建立在最小的接口上,否則將會造成接口污染;類A通過接口I依賴類B,類C通過接口I依賴類D,如果接口I對於類A和類B來說不是最小接口,則類B和類D必須去實現它們不需要的方法;
原則的含義是:建立單一接口,不要建立龐大臃腫的接口,盡量細化接口,接口中的方法盡量少;就是說,我們要為每個類建立專用的接口,而不要試圖去建立一個龐大的接口供所有依賴它的類去調用;
注意,接口盡量小,但是要有限度,對接口進行細化可以提高程序設計靈活性,但是如果過小,則會導致接口數量盡量小,使設計復雜化。所以一定要適度,為依賴接口的類定制服務,只暴露給調用的類它需要的方法,它不需要的方法則隱藏起來;
規則:
* 一個接口只服務於一個子模塊或業務邏輯,服務定制;
* 通過業務邏輯壓縮接口中的public方法,讓接口看起來更加精悍;
* 已經被污染了的接口,盡量修改,如果變更風險太大,則用適配器模式進行轉化;
* 根據具體的業務,深入了解邏輯,用心感知去控制設計思路;
如何實施接口隔離,主要有兩種方法:
1. 委托分離,通過增加一個新的接口類型來委托客戶的請求,隔離客戶和接口的直接依賴,注意這同時也會增加系統的開銷;
2. 多重繼承分離,通過接口的多重繼承來實現客戶的需求;
迪米特法則
定義:一個對象應該對其他對象保持最少的了解,其核心精神就是:不和陌生人說話,通俗之意就是一個對象對自己需要耦合關聯調用的類應該知道的少;這會導致類之間的耦合度降低,每個類都盡量減少對其他類的依賴。
合成復用原則
原則是盡量使用合成/聚合的方式,而不是使用繼承;
開閉原則
定義:一個軟件實體如類、模版和函數應該對擴展,對修改關閉;
解決方案:當軟件需要變化時,盡量通過擴展軟件實體的行為來實現變化,而不是修改已有的代碼來實現變化;
- 單一職責原則:實現類要職責單一;
- 里氏替換原則:不要破壞繼承體系;
- 依賴倒置原則:面向接口編程;
- 接口隔離原則:設計接口的時候要精簡單一;
- 迪米特法則:降低耦合;
開閉原則:總綱,對擴展開放,對修改關閉;