1. 程式人生 > >設計模式原則(3)--Dependency Inversion Principle(DIP)--依賴倒轉原則

設計模式原則(3)--Dependency Inversion Principle(DIP)--依賴倒轉原則

以及 .get 依賴註入 不能 通過 而是 耦合度 面向實現 看書

1.定義:

  高層模塊不應該依賴低層模塊,二者都應該依賴其抽象;抽象不應該依賴細節;細節應該依賴抽象。   抽象不應該依賴於細節,細節應當依賴於抽象。換言之,要針對接口編程,而不是針對實現編程。

2.使用場景:

  類A直接依賴類B,假如要將類A改為依賴類C,則必須通過修改類A的代碼來達成。這種場景下,類A一般是高層模塊,負責復雜的業務邏輯;類B和類C是低層模塊,負責基本的原子操作;假如修改類A,會給程序帶來不必要的風險。即將類A修改為依賴接口I,類B和類C各自實現接口I,類A通過接口I間接與類B或者類C發生聯系,則會大大降低修改類A的幾率。

3.使用特點

  依賴倒置原則基於這樣一個事實:相對於細節的多變性,抽象的東西要穩定的多。以抽象為基礎搭建起來的架構比以細節為基礎搭建起來的架構要穩定的多。在java中,抽象指的是接口或者抽象類,細節就是具體的實現類,使用接口或者抽象類的目的是制定好規範和契約,而不去涉及任何具體的操作,把展現細節的任務交給他們的實現類去完成。   依賴倒轉原則
要求我們在程序代碼中傳遞參數時或在關聯關系中,盡量引用層次高的抽象層類,即使用接口和抽象類進行變量類型聲明、參數類型聲明、方法返回類型聲明,以及數據類型的轉換等,而不要用具體類來做這些事情。為了確保該原則的應用,一個具體類應當只實現接口或抽象類中聲明過的方法,而不要給出多余的方法,否則將無法調用到在子類中增加的新方法。 在引入抽象層後,系統將具有很好的靈活性,在程序中盡量使用抽象層進行編程,而將具體類寫在配置文件中,這樣一來,如果系統行為發生變化,只需要對抽象層進行擴展,並修改配置文件,而無須修改原有系統的源代碼,在不修改的情況下來擴展系統的功能,滿足開閉原則的要求。 在實現依賴倒轉原則時,我們需要針對抽象層編程,而將具體類的對象通過依賴註入(DependencyInjection, DI)的方式註入到其他對象中,依賴註入是指當一個對象要與其他對象發生依賴關系時,通過抽象來註入所依賴的對象。常用的註入方式有三種,分別是:構造註入,設值註入(Setter註入)和接口註入。構造註入是指通過構造函數來傳入具體類的對象,設值註入是指通過Setter方法來傳入具體類的對象,而接口註入是指通過在接口中聲明的業務方法來傳入具體類的對象。這些方法在定義時使用的是抽象類型,在運行時再傳入具體類型的對象,由子類對象來覆蓋父類對象。

4.實現案例

  依賴倒置原則的核心思想是面向接口編程,我們依舊用一個例子來說明面向接口編程比相對於面向實現編程好在什麽地方。場景是這樣的:閑著無聊看書。代碼如下:
class Book{
    public String getContent(){
        return "書中的故事";
    }
}
class Mother{
    public void narrate(Book book){
        System.out.println("閑著無聊");
        System.out.println(book.getContent());
    }
}
public class Client{ public static void main(String[] args){ Mother mother = new Mother(); mother.narrate(new Book()); } }
運行結果: 閑著無聊 書中的故事   運行良好,假如有一天,需求變成這樣:不是給書而是給一份報紙,報紙的代碼如下:
class Newspaper{
  public String getContent(){
    return "報紙上的新聞";
  }
}

  這時候竟然不能讀報紙! 只是將書換成報紙,居然必須要修改Mother才能讀。假如以後需求換成雜誌呢?換成網頁呢?還要不斷地修改Mother,這顯然不是好的設計。原因就是Mother與Book之間的耦合性太高了,必須降低他們之間的耦合度才行。

  我們引入一個抽象的接口IReader。讀物,只要是帶字的都屬於讀物:
interface IReader{
    public String getContent();
}
  Mother類與接口IReader發生依賴關系,而Book和Newspaper都屬於讀物的範疇,他們各自都去實現IReader接口,這樣就符合依賴倒置原則了,代碼修改為:
class Newspaper implements IReader {
  public String getContent(){
    return "報紙上的新聞";
  }
}
class Book implements IReader{
  public String getContent(){
    return "書中的故事";
  }
}
class Mother{
  public void narrate(IReader reader){
    System.out.println("閑著無聊");
    System.out.println(reader.getContent());
  }
}
public class Client{
  public static void main(String[] args){
    Mother mother = new Mother();
    mother.narrate(new Book());
    mother.narrate(new Newspaper());
  }
}

運行結果:

閑著無聊 書中的故事 閑著無聊 報紙上的新聞   這樣修改後,無論以後怎樣擴展Client類,都不需要再修改Mother類了。這只是一個簡單的例子,實際情況中,代表高層模塊的Mother類將負責完成主要的業務邏輯,一旦需要對它進行修改,引入錯誤的風險極大。所以遵循依賴倒置原則可以降低類之間的耦合性,提高系統的穩定性,降低修改程序造成的風險。   采用依賴倒置原則給多人並行開發帶來了極大的便利,比如上例中,原本Mother類與Book類直接耦合時,Mother類必須等Book類編碼完成後才可以進行編碼,因為Mother類依賴於Book類。修改後的程序則可以同時開工,互不影響,因為Mother與Book類一點關系也沒有。參與協作開發的人越多、項目越龐大,采用依賴導致原則的意義就越重大。現在很流行的TDD開發模式就是依賴倒置原則最成功的應用。

5.註意事項

  傳遞依賴關系有三種方式,以上的例子中使用的方法是接口傳遞,另外還有兩種傳遞方式:構造方法傳遞和setter方法傳遞,相信用過Spring框架的,對依賴的傳遞方式一定不會陌生。 在實際編程中,我們一般需要做到如下3點: (1)低層模塊盡量都要有抽象類或接口,或者兩者都有。 (2)變量的聲明類型盡量是抽象類或接口。 (3)使用繼承時遵循裏氏替換原則。   依賴倒置原則的核心就是要我們面向接口編程,理解了面向接口編程,也就理解了依賴倒置。

設計模式原則(3)--Dependency Inversion Principle(DIP)--依賴倒轉原則