1. 程式人生 > >(轉)設計模式六大原則(3):依賴倒置原則

(轉)設計模式六大原則(3):依賴倒置原則

定義高層模組不應該依賴低層模組,二者都應該依賴其抽象;抽象不應該依賴細節;細節應該依賴抽象。

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

解決方案:將類A修改為依賴介面I,類B和類C各自實現介面I,類A通過介面I間接與類B或者類C發生聯絡,則會大大降低修改類A的機率。

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

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

  1. class Book{  
  2.     public String getContent(){  
  3.         return"很久很久以前有一個阿拉伯的故事……";  
  4.     }  
  5. }  
  6. class Mother{  
  7.     publicvoid narrate(Book book){  
  8.         System.out.println("媽媽開始講故事"
    );  
  9.         System.out.println(book.getContent());  
  10.     }  
  11. }  
  12. publicclass Client{  
  13.     publicstaticvoid main(String[] args){  
  14.         Mother mother = new Mother();  
  15.         mother.narrate(new Book());  
  16.     }  
  17. }  

執行結果:

媽媽開始講故事
很久很久以前有一個阿拉伯的故事……

        執行良好,假如有一天,需求變成這樣:不是給書而是給一份報紙,讓這位母親講一下報紙上的故事,報紙的程式碼如下:

  1. class Newspaper{  
  2.     public String getContent(){  
  3.         return"林書豪38+7領導尼克斯擊敗湖人……";  
  4.     }  
  5. }  

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

我們引入一個抽象的介面IReader。讀物,只要是帶字的都屬於讀物:

  1. interface IReader{  
  2.     public String getContent();  
  3. }  

Mother類與介面IReader發生依賴關係,而Book和Newspaper都屬於讀物的範疇,他們各自都去實現IReader介面,這樣就符合依賴倒置原則了,程式碼修改為:

  1. class Newspaper implements IReader {  
  2.     public String getContent(){  
  3.         return"林書豪17+9助尼克斯擊敗老鷹……";  
  4.     }  
  5. }  
  6. class Book implements IReader{  
  7.     public String getContent(){  
  8.         return"很久很久以前有一個阿拉伯的故事……";  
  9.     }  
  10. }  
  11. class Mother{  
  12.     publicvoid narrate(IReader reader){  
  13.         System.out.println("媽媽開始講故事");  
  14.         System.out.println(reader.getContent());  
  15.     }  
  16. }  
  17. publicclass Client{  
  18.     publicstaticvoid main(String[] args){  
  19.         Mother mother = new Mother();  
  20.         mother.narrate(new Book());  
  21.         mother.narrate(new Newspaper());  
  22.     }  
  23. }  

執行結果:

媽媽開始講故事
很久很久以前有一個阿拉伯的故事……
媽媽開始講故事
林書豪17+9助尼克斯擊敗老鷹……

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

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

         傳遞依賴關係有三種方式,以上的例子中使用的方法是介面傳遞,另外還有兩種傳遞方式:構造方法傳遞setter方法傳遞,相信用過Spring框架的,對依賴的傳遞方式一定不會陌生。
在實際程式設計中,我們一般需要做到如下3點:

  • 低層模組儘量都要有抽象類或介面,或者兩者都有。
  • 變數的宣告型別儘量是抽象類或介面。
  • 使用繼承時遵循里氏替換原則。

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