1. 單一職責原則(SRP)
(1)概念
單一職責原則的定義是:應該有且只有一個原因引起類的改變,即一個類只負責一個職責。
比如讓類C負責兩個不同的職責:職責P1,P2。當由於職責P1需求發生改變而需要修改類C時,有可能會導致原本運行正常的職責P2功能發生故障。
(2)舉例
關於用戶管理的一個類按如下類圖來設計:
很顯然,用戶的屬性和行為沒有分開,按照單一職責原則,應該將其重新拆封成兩個接口:用戶屬性接口IUserBO,用戶行為接口IUserBiz。
分清職責之后的代碼如下:
...... IUserInfo userInfo = new UserInfo(); // 操作用戶的屬性 IUserBo userBo = (IUserBo)userInfo; userBo.setPassword("123"); // 操作用戶的行為 IUserBiz userBiz = (IUserBiz)userInfo; userBiz.delete(userBo); ......
(3)總結
單一職責原則適用於接口、類、方法,一個方法盡可能地完成一件事情。比如一個修改密碼的方法,就不要把它放在修改用戶信息這個大的方法中去,否則方法的職責就不清晰了。但是過分細分類的職責又會人為的增加系統的復雜性,比如本來一個類可以實現的行為硬拆分為兩個類,然后再使用聚合或者組合的方式耦合在一起,就人為的制造了麻煩。由於單一的職責這個“職責”沒有一個量化的標准,所以最難划分的還是職責,這個還是要根據實際情況和個人經驗來定。
2. 里氏替換原則(LSP)
(1)概念
里氏替換原則針對的對象是具有繼承關系的子類和父類。
里氏替換原則的定義是:只要父類出現的地方子類就可以出現,而且將其替換為子類也不會產生任何出錯或者異常。
子類必須完全實現父類的方法(方法不能為空)。即父類的方法必須是子類全部需要的,如果不是全部需要的,就違背了LSP原則。
在類中調用其他類時必須使用父類或者接口,如果不使用父類或者接口,則類的設計違背了LSP原則。
(2)舉例
某公司有普通用戶和vip用戶,他們發郵件的過程如下:
分析發現普通用戶和vip用戶發郵件的過程是相同的,即兩個send()方法重復。將來還可能增加用戶新類型,為了讓系統具有更好的擴展性,使用里氏替換原則進行重構:
(3)總結
里氏替換原則包含以下4層含義:
子類必須完全實現父類的方法(方法體不為空);
子類可以有自己特有的方法;
重寫父類的方法時輸入參數可以被放大(子類中重寫父類方法的前置條件必須與父類中被重寫方法的前置條件相同或者更寬松);
重寫父類的方法時輸出結果可以被縮小(子類中重寫父類方法的返回值必須小於等於父類中被重寫方法的返回值)。
當然如果在編程過程中違反了LSP原則在運行時也不會出現什么問題,但是會遺留下很多潛在的問題會給以后的變更、維護工作造成很大的困難。
3. 依賴倒置原則(DIP)
(1)概念
依賴倒置原則的定義是:實現類之間不發生直接的依賴關系,其依賴關系是通過接口或者抽象類產生的。即面向接口編程。
實現類依賴接口或者抽象類,而接口或者抽象類不依賴於實現類。
(2)舉例
司機開奔馳車的類圖如下:
實現代碼如下:
// 司機的實現類 public class Driver { public void drive(Benz benz) { benz.run(); } }
// 奔馳車的實現類 public class Benz { public void run() { System.out.println("奔馳車出發..."); } }
// 場景調用類 public class Scene { public static void main(String[] args) { Driver driver = new Driver(); Benz benz = new Benz(); driver.drive(benz); } }
看起來好像這樣設計是沒有任何問題的,但我們常說“危難時刻見真情“,在技術上即“變更才顯真功夫”。現在司機不僅要開奔馳,要開寶馬車。但司機driver只有開奔馳的方法,而沒有開動寶馬車的方法啊,這就不合理了。這里只是增加了一個車類就要修改司機類,這是不穩定的、易變的。引用依賴倒置原則,采用面向接口編程的思想設計類圖如下:
實現代碼如下:
// 司機接口類 public interface IDriver { public void drive(ICar car); }
// 汽車接口類 public interface ICar { public void run(); }
// 司機的實現類 public class Driver implements IDriver { @Override public void drive(ICar car) { car.run(); } }
// 奔馳車的實現類 public class Benz implements ICar { @Override public void run() { System.out.println("奔馳車出發..."); } }
// 寶馬車的實現類 public class BMW implements ICar { @Override public void run() { System.out.println("寶馬車出發..."); } }
(3)總結
依賴倒置原則的本質就是通過抽象(接口或抽象類)使各個類或模塊的實現彼此獨立,不相互影響,實現模塊間的松耦合。
每個類應該盡量都有接口或者抽象類,或者兩者都有。
變量的表面類型盡量是接口或者抽象類。
依賴倒置原則使類之間不存在依賴關系,可以進行獨立的並行開發,而且兩個類的單元測試也可以獨立地運行。
6大設計原則詳解(二):http://www.cnblogs.com/LangZXG/p/6242927.html
6大設計原則,與常見設計模式(概述):http://www.cnblogs.com/LangZXG/p/6204142.html
類圖基礎知識:http://www.cnblogs.com/LangZXG/p/6208716.html