天天看點

[設計模式]面向對象設計原則之依賴倒置原則

依賴倒置原則的由來

類A直接依賴類B,假如要将類A改為依賴類C,則必須通過修改類A的代碼來達成。這種場景下,類A一般是高層子產品,負責複雜的業務邏輯;類B和類C是低層子產品,負責基本的原子操作;假如修改類A,會給程式帶來不必要的風險。

其實 類A就像我們寫的action類,負責複雜的邏輯處理,而類B類C就是底層的比如:資料庫操作類,方法幫助類。
           

什麼是依賴倒置原則?

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

    也就是說:

    • 子產品間的依賴是通過抽象發生,實作類之間不發生直接的依賴關系,其依賴關系是通過接口或抽象類産生的;
    • 接口或抽象類不依賴于實作類;
    • 實作類依賴接口或抽象類。

      更加精簡的定義就是“面向接口程式設計”

      (這裡要對剛入門的小白提一個問題了:面向接口程式設計是否與面向對象程式設計相悖?) 結尾解答!

對于職責的了解

依賴倒置原則基于這樣一個事實:相對于細節的多變性,抽象的東西要穩定的多。以抽象為基礎搭建起來的架構比以細節為基礎搭建起來的架構要穩定的多。在java中,抽象指的是接口或者抽象類,細節就是具體的實作類,使用接口或者抽象類的目的是制定好規範和契約,而不去涉及任何具體的操作,把展現細節的任務交給他們的實作類去完成。

依賴倒置原則的核心思想是面向接口程式設計,我們依舊用一個例子來說明面向接口程式設計比相對于面向實作程式設計好在什麼地方。場景是這樣的,母親給孩子講故事,隻要給她一本書,她就可以照着書給孩子講故事了。代碼如下:

例子永遠是Java中最好了解的方式

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 "林書豪38+7上司尼克斯擊敗湖人……";  
    }  
}  
           

這位母親卻辦不到,因為她居然不會讀報紙上的故事,這太荒唐了,隻是将書換成報紙,居然必須要修改Mother才能讀。假如以後需求換成雜志呢?換成網頁呢?還要不斷地修改Mother,這顯然不是好的設計。原因就是Mother與Book之間的耦合性太高了,必須降低他們之間的耦合度才行。

我們引入一個抽象的接口IReader。讀物,隻要是帶字的都屬于讀物:

interface IReader{  
    public String getContent();  
}  
           

Mother類與接口IReader發生依賴關系,而Book和Newspaper都屬于讀物的範疇,他們各自都去實作IReader接口,這樣就符合依賴倒置原則了,代碼修改為:

class Newspaper implements IReader {  
    public String getContent(){  
        return "林書豪17+9助尼克斯擊敗老鷹……";  
    }  
}  
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());  
    }  
}  
           

運作結果:

媽媽開始講故事

很久很久以前有一個阿拉伯的故事……

媽媽開始講故事

林書豪17+9助尼克斯擊敗老鷹……

這樣修改後,無論以後怎樣擴充Client類,都不需要再修改Mother類了。這隻是一個簡單的例子,實際情況中,代表高層子產品的Mother類将負責完成主要的業務邏輯,一旦需要對它進行修改,引入錯誤的風險極大。是以遵循依賴倒置原則可以降低類之間的耦合性,提高系統的穩定性,降低修改程式造成的風險。

采用依賴倒置原則給多人并行開發帶來了極大的便利,比如上例中,原本Mother類與Book類直接耦合時,Mother類必須等Book類編碼完成後才可以進行編碼,因為Mother類依賴于Book類。修改後的程式則可以同時開工,互不影響,因為Mother與Book類一點關系也沒有。參與協作開發的人越多、項目越龐大,采用依賴導緻原則的意義就越重大。現在很流行的TDD開發模式就是依賴倒置原則最成功的應用。

傳遞依賴關系有三種方式,以上的例子中使用的方法是接口傳遞,另外還有兩種傳遞方式:構造方法傳遞和setter方法傳遞,相信用過Spring架構的,對依賴的傳遞方式一定不會陌生。

如何遵循依賴倒置原則

在實際程式設計中,我們一般需要做到如下3點:

  • 低層子產品盡量都要有抽象類或接口,或者兩者都有。
  • 變量的聲明類型盡量是抽象類或接口。
  • 使用繼承時遵循裡氏替換原則。

    依賴倒置原則的核心就是要我們面向接口程式設計,了解了面向接口程式設計,也就了解了依賴倒置。

ex:面向對象是程式設計思想,面向接口是架構實作方式,可不能夠混為一談!具體的探讨在這裡就不深入了。(就這麼點東西,為什麼非要寫在最後?)

學習程式設計,很多東西想想你是否知道它的原理和實作,而不是簡單的接收,會用。對于一些說法要勇敢的去提問,去質疑,而不是:”噢,面向接口程式設計,記住了!”,或者:“面向接口程式設計?…….. 不理它”。

思考才會讓你學習到更多的東西,而不是每個月拿着幾千的工資隻會搬磚的碼農。夢想很近也很遠,讓我們一起加油!