1. 程式人生 > >《設計模式之禪》之——六大設計原則解讀

《設計模式之禪》之——六大設計原則解讀

面向對象 文件 引用 serial 輸出 ble 實例化 客戶 壟斷

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

1. 單一職責原則定義

應該有且僅有一個原因引起類的變更。

2. 單一職責原則好處

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

3. 單一職責適用於接口、類,同時也適用於方法

對於接口,我們在設計的時候一定要做到單一,但是對於實現類就需要多方面考慮了。生搬硬套單一職責原則會引起類的劇增,給維護帶來非常多的麻煩,而且過分細分類的職責也會人為地增加系統的復雜性。本來一個類可以實現的行為硬要拆成兩個類,然後再使用聚合或組合的方式耦合在一起,人為制造了系統的復雜性。所以原則是死的,人是活的,這句話很有道理。

類的單一職責確實受非常多因素的制約,純理論地來講,這個原則是非常優秀的,但是現實有現實的難處,你必須去考慮項目工期、成本、人員技術水平、硬件情況、網絡情況甚至有時候還要考慮政府政策、壟斷協議等因素。

對於單一職責原則,我的建議是接口一定要做到單一職責,類的設計盡量做到只有一個原因引起變化。

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

引入裏氏替換原則來減少面向對象的語言 “繼承” 的缺點

1. 繼承的缺點

繼承是侵入性的。只要繼承,就必須擁有父類的所有屬性和方法;

降低代碼的靈活性。子類必須擁有父類的屬性和方法,讓子類自由的世界中多了些約束;

增強了耦合性。當父類的常量、變量和方法被修改時,必需要考慮子類的修改,而且在缺乏規範的環境下,這種修改可能帶來非常糟糕的結果——大片的代碼需要重構。

2. 裏氏替換原則定義

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

2.1 包含四層含義

  • 子類必須完全實現父類的方法
  • 子類可以有自己的個性
  • 覆蓋或實現父類的方法時輸入參數可以被放大
  • 覆寫或實現父類的方法時輸出結果可以被縮小

采用裏氏替換原則的目的就是增強程序的健壯性,版本升級時也可以保持非常好的兼容性。即使增加子類,原有的子類還可以繼續運行。

3. 項目應用

在實際項目中,每個子類對應不同的業務含義,使用父類作為參數,傳遞不同的子類完成不同的業務邏輯。

在項目中,采用裏氏替換原則時,盡量避免子類的“個性”,一旦子類有“個性”,這個子類和父類之間的關系就很難調和了,把子類當做父類使用,子類的“個性”被抹殺——委屈了點;把子類單獨作為一個業務來使用,則會讓代碼間的耦合關系變得撲朔迷離——缺乏類替換的標準。

三、依賴倒置原則(Dependence Inversion Principle,DIP)

1. 原始定義

  • 高層模塊不應該依賴低層模塊,兩者都應該依賴其抽象;
  • 抽象不應該依賴細節;
  • 細節應該依賴抽象。

*名詞解釋:

每一個邏輯的實現都是由原子邏輯組成的,不可分割的原子邏輯就是低層模塊,原子邏輯的再組裝就是高層模塊。

在Java語言中,抽象就是指接口或抽象類,兩者都是不能直接被實例化的;細節就是實現類,實現接口或繼承抽象類而產生的類就是細節,其特點就是可以直接被實例化,也就是可以加上一個關鍵字new產生一個對象。

2. 在Java語言中的表現

  • 模塊間的依賴通過抽象發生,實現類之間不發生直接的依賴關系,其依賴關系是通過接口或抽象類產生的;
  • 接口或抽象類不依賴於實現類;
  • 實現類依賴接口或抽象類。

3. 依賴倒置原則好處

依賴倒置原則的本質就是通過抽象(接口或抽象類)使各個類或模塊的實現彼此獨立,不互相影響,實現模塊間的松耦合。

采用依賴倒置原則可以減少類間的耦合性,提高系統的穩定性,降低並行開發引起的風險,提高代碼的可讀性和可維護性。

兩個類之間有依賴關系,只要制定出兩者之間的接口(或抽象類)就可以獨立開發了,而且項目之間的單元測試也可以獨立地運行,而TDD(Test-Driven Development,測試驅動開發)開發模式就是依賴倒置原則的最高級應用。

4. 依賴的三種寫法

  • 構造函數傳遞依賴對象
  • Setter方法傳遞依賴對象
  • 接口聲明依賴對象(接口註入)

5. 項目應用

需要遵循以下的幾個規則

  • 每個類盡量都有接口或抽象類,或者抽象類和接口兩者都具備
  • 變量的表面類型盡量是接口或者是抽象類
  • 任何類都不應該從具體類派生
  • 盡量不要覆寫基類的方法
  • 結合裏氏替換原則使用(接口負責定義public屬性和方法,並且聲明與其他對象的依賴關系,抽象類負責公共構造部分的實現,實現類準確的實現業務邏輯,同時在適當的時候對父類進行細化。)

在項目中,大家只要記住是“面向接口編程”就基本上抓住了依賴倒置原則的核心。

四、接口隔離原則(Interface Segregation Principle)

1. 接口隔離原則的定義

  • 客戶端不應該依賴它不需要的接口;
  • 類間的依賴關系應該建立在最小的接口上。

即:建立單一接口,不要建立臃腫龐大的接口。再通俗一點講:接口盡量細化,同時接口中的方法盡量少。(防止封裝過度)

1.1 接口隔離原則包含四層含義

  • 接口要盡量小(根據接口隔離原則拆分接口時,首先必須滿足單一職責原則。)
  • 接口要高內聚(高內聚就是提高接口、類、模塊的處理能力,減少對外的交互。)
  • 定制服務(采用定制服務就必然有一個要求:只提供訪問者需要的方法)
  • 接口設計是有限度的(接口的設計粒度越小,系統越靈活,這是不爭的事實。但是,靈活的同時也帶來了結構的復雜化,開發難度增加,可維護性降低,)

2. 接口隔離原則優點

接口是我們設計時對外提供的契約,通過分散定義多個接口,可以預防未來變更的擴散,提高系統的靈活性和可維護性。

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

1. 迪米特法則定義

一個對象應該對其他對象有最少的了解

1.1 迪米特法則包含四層含義

  • 只和朋友交流(類與類之間的關系是建立在類間的,而不是方法間,因此一個方法盡量不引入一個類中不存在的對象,當然,JDK API提供的類除外)
  • 朋友間也是有距離的(為了保持朋友類間的距離,在設計時需要反復衡量:是否還可以再減少public方法和屬性,是否可以修改為private、package-private、protected等訪問權限,是否可以加上final關鍵字等)
  • 是自己的就是自己的(如果一個方法放在本類中,即不增加類間關系,也對本類不產生負面影響,就放置在本類中。)
  • 謹慎使用Serializable(防止對象實現Serializable接口後屬性訪問權限的擴大在客戶端與服務器端的不一致)

2. 迪米特法則使用

迪米特法則的核心觀念就是類間解耦,弱耦合,只有弱耦合了以後,類的復用率才可以提高。其要求的結果就是產生了大量的中轉或跳轉類,導致系統的復雜性提高,同時也為維護帶來了難度。讀者在采用迪米特法則時需要反復權衡,既做到讓結構清晰,又做到高內聚低耦合。

六、開閉原則(Open Closed Principle)

1. 開閉原則定義

一個軟件實體如類、模塊和函數應該對擴展開放,對修改關閉。

2. 開閉原則好處

  • 開閉原則可以提高復用性
  • 開閉原則可以提高可維護性

3. 使用開閉原則

抽象約束(包含三層含義:第一,通過接口或抽象類約束擴展,對擴展進行邊界限定,不允許出現在接口或抽象類中不存在的public方法;第二,參數類型、引用對象盡量使用接口或者抽象類,而不是實現類;第三,抽象層盡量保持穩定,一旦確定即不允許修改。)

元數據(配置參數)控制模塊行為(通過擴展一個子類,修改配置文件,完成業務變化)

制定項目章程

封裝變化(包含兩層含義:第一,將相同的變化封裝到一個接口或抽象類中;第二,將不同的變化封裝到不同的接口或抽象類中,不應該有兩個不同的變化出現在同一個接口或抽象類中。)

開閉原則是一個終極目標,任何人包括大師級人物都無法百分之百做到,但朝這個方向努力,可以非常顯著地改善一個系統的架構,真正做到“擁抱變化”。

《設計模式之禪》之——六大設計原則解讀