天天看點

6大設計原則詳解(一)

1. 單一職責原則(SRP)

(1)概念

單一職責原則的定義是:應該有且隻有一個原因引起類的改變,即一個類隻負責一個職責。

比如讓類C負責兩個不同的職責:職責P1,P2。當由于職責P1需求發生改變而需要修改類C時,有可能會導緻原本運作正常的職責P2功能發生故障。

(2)舉例

關于使用者管理的一個類按如下類圖來設計:

6大設計原則詳解(一)
很顯然,使用者的屬性和行為沒有分開,按照單一職責原則,應該将其重新拆封成兩個接口:使用者屬性接口IUserBO,使用者行為接口IUserBiz。
6大設計原則詳解(一)
厘清職責之後的代碼如下:

......
IUserInfo userInfo = new UserInfo();
// 操作使用者的屬性
IUserBo userBo = (IUserBo)userInfo;
userBo.setPassword("123");
// 操作使用者的行為
IUserBiz userBiz = (IUserBiz)userInfo;
userBiz.delete(userBo);
......      

(3)總結

單一職責原則适用于接口、類、方法,一個方法盡可能地完成一件事情。比如一個修改密碼的方法,就不要把它放在修改使用者資訊這個大的方法中去,否則方法的職責就不清晰了。但是過分細分類的職責又會人為的增加系統的複雜性,比如本來一個類可以實作的行為硬拆分為兩個類,然後再使用聚合或者組合的方式耦合在一起,就人為的制造了麻煩。由于單一的職責這個“職責”沒有一個量化的标準,是以最難劃分的還是職責,這個還是要根據實際情況和個人經驗來定。

2. 裡氏替換原則(LSP)

裡氏替換原則針對的對象是具有繼承關系的子類和父類。

裡氏替換原則的定義是:隻要父類出現的地方子類就可以出現,而且将其替換為子類也不會産生任何出錯或者異常。

子類必須完全實作父類的方法(方法不能為空)。即父類的方法必須是子類全部需要的,如果不是全部需要的,就違背了LSP原則。

在類中調用其他類時必須使用父類或者接口,如果不使用父類或者接口,則類的設計違背了LSP原則。

某公司有普通使用者和vip使用者,他們發郵件的過程如下:

6大設計原則詳解(一)

分析發現普通使用者和vip使用者發郵件的過程是相同的,即兩個send()方法重複。将來還可能增加使用者新類型,為了讓系統具有更好的擴充性,使用裡氏替換原則進行重構:

6大設計原則詳解(一)

裡氏替換原則包含以下4層含義:

子類必須完全實作父類的方法(方法體不為空);

子類可以有自己特有的方法;

重寫父類的方法時輸入參數可以被放大(子類中重寫父類方法的前置條件必須與父類中被重寫方法的前置條件相同或者更寬松);

重寫父類的方法時輸出結果可以被縮小(子類中重寫父類方法的傳回值必須小于等于父類中被重寫方法的傳回值)。

當然如果在程式設計過程中違反了LSP原則在運作時也不會出現什麼問題,但是會遺留下很多潛在的問題會給以後的變更、維護工作造成很大的困難。

3. 依賴倒置原則(DIP)

依賴倒置原則的定義是:實作類之間不發生直接的依賴關系,其依賴關系是通過接口或者抽象類産生的。即面向接口程式設計。

實作類依賴接口或者抽象類,而接口或者抽象類不依賴于實作類。

司機開奔馳車的類圖如下:

6大設計原則詳解(一)

實作代碼如下:

// 司機的實作類
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隻有開奔馳的方法,而沒有開動寶馬車的方法啊,這就不合理了。這裡隻是增加了一個車類就要修改司機類,這是不穩定的、易變的。引用依賴倒置原則,采用面向接口程式設計的思想設計類圖如下:

6大設計原則詳解(一)
// 司機接口類
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("寶馬車出發...");
    }
}      

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

每個類應該盡量都有接口或者抽象類,或者兩者都有。

變量的表面類型盡量是接口或者抽象類。

依賴倒置原則使類之間不存在依賴關系,可以進行獨立的并行開發,而且兩個類的單元測試也可以獨立地運作。

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

注:轉載請注明出處   http://www.cnblogs.com/LangZXG/p/6242925.html

繼續閱讀