談一談依賴倒置原則

爲得到良好的閱讀體驗,請訪問原文:傳送門html

1、概念


依賴倒置原則(Dependence Inversion Principle,DIP)是指設計代碼結構時,高層模塊不該該依賴低層模塊,兩者都應該依賴其抽象。java

抽象不該該依賴細節,細節應該依賴抽象。經過依賴倒置,能夠減小類與類之間的耦合性,提升系統的穩定性,提升代碼的可讀性和可維護性,而且可以下降修改程序所形成的風險。git

2、爲何


先來看一個例子

但是依賴倒置原則是怎麼作到的呢?咱們先來看一個例子:一個愛學習的「我沒有三顆心臟」同窗如今正在學習「設計模式」和「Java」的課程,僞代碼以下:github

public class Wmyskxz {

    public void studyJavaCourse() {
        System.out.println("「我沒有三顆心臟」同窗正在學習「Java」課程");
    }

    public void studyDesignPatternCourse() {
        System.out.println("「我沒有三顆心臟」同窗正在學習「設計模式」課程");
    }
}

咱們來模擬上層調用一下:編程

public static void main(String[] args) {
    Wmyskxz wmyskxz = new Wmyskxz();
    wmyskxz.studyJavaCourse();
    wmyskxz.studyDesignPatternCourse();
}

緣由一:有效控制影響範圍

因爲「我沒有三顆心臟」同窗熱愛學習,隨着學習興趣的 「暴增」,可能會繼續學習 AI(人工智能)的課程。這個時候,由於「業務的擴展」,要從底層實現到高層調用依次地修改代碼。設計模式

咱們須要在 Wmyskxz 類中新增 studyAICourse() 方法,也須要在高層調用中增長調用,這樣一來,系統發佈後,實際上是很是不穩定的。顯然在這個簡單的例子中,咱們還能夠自信地認爲,咱們能 Hold 住這一次的修改帶來的影響,由於都是新增的代碼,咱們迴歸的時候也能夠很好地 cover 住,但實際的狀況和實際的軟件環境要複雜得多。微信

最理想的狀況就是,咱們已經編寫好的代碼能夠 「萬年不變」,這就意味着已經覆蓋的單元測試能夠不用修改,已經存在的行爲能夠保證保持不變,這就意味着「穩定」。任何代碼上的修改帶來的影響都是有未知風險的,不論看上去多麼簡單。架構

緣由二:加強代碼可讀性和可維護性

另一點,你有沒有發現其實加上新增的 AI 課程的學習,他們三節課本質上行爲都是同樣的,若是咱們任由這樣行爲近乎同樣的代碼在咱們的類裏面肆意擴展的話,很快咱們的類就會變得臃腫不堪,等到咱們意識到不得不重構這個類以緩解這樣的狀況的時候,或許成本已經變得高得可怕了。ide

緣由三:下降耦合

《資本論》中有這樣一段描述:單元測試

在商品經濟的萌芽時期,出現了物物交換。假設你要買一個 iPhone,賣 iPhone 的老闆讓你拿一頭豬跟他換,但是你並無養豬,你只會編程。因此你找到一位養豬戶,說給他作一個養豬的 APP 來換他一頭豬,他說換豬能夠,可是得用一條金項鍊來換...

因此這裏就出現了一連串的對象依賴,從而形成了嚴重的耦合災難。解決這個問題的最好的辦法就是,買賣雙發都依賴於抽象——也就是貨幣——來進行交換,這樣一來耦合度就大爲下降了。

3、怎麼作


咱們如今的代碼是上層直接依賴低層實現,如今咱們須要定義一個抽象的 ICourse 接口,來對這種強依賴進行解耦(就像上面《資本論》中的例子那樣):

接下來咱們能夠參考一下僞代碼,先定一個課程的抽象 ICourse 接口:

public interface ICourse {
    void study();
}

而後編寫分別爲 JavaCourseDesignPatternCourse 編寫一個類:

public class JavaCourse implements ICourse {

    @Override
    public void study() {
        System.out.println("「我沒有三顆心臟」同窗正在學習「Java」課程");
    }
}

public class DesignPatternCourse implements ICourse {

    @Override
    public void study() {
        System.out.println("「我沒有三顆心臟」同窗正在學習「設計模式」課程");
    }
}

而後把 Wmyskxz 類改形成以下的樣子:

public class Wmyskxz {

    public void study(ICourse course) {
        course.study();
    }
}

再來是咱們的調用:

public static void main(String[] args) {
    Wmyskxz wmyskxz = new Wmyskxz();
    wmyskxz.study(new JavaCourse());
    wmyskxz.study(new DesignPatternCourse());
}

這時候咱們再來看代碼,不管「我沒有三顆心臟」的興趣怎麼暴漲,對於新的課程,都只須要新建一個類,經過參數傳遞的方式告訴它,而不須要修改底層的代碼。實際上這有點像你們熟悉的依賴注入的方式了。

總之,切記:以抽象爲基準比以細節爲基準搭建起來的架構要穩定得多,所以在拿到需求後,要面相接口編程,先頂層設計再細節地設計代碼結構。

參考資料


  1. http://www.javashuo.com/article/p-xurntreo-gp.html - 那些年搞不懂的高深術語——依賴倒置•控制反轉•依賴注入•面向接口編程
  2. 《Spring 5 核心原理 與 30 個類手寫實戰》 - 譚勇德 著

按照慣例黏一個尾巴:

歡迎轉載,轉載請註明出處!
獨立域名博客:wmyskxz.com
簡書ID:@我沒有三顆心臟
github:wmyskxz 歡迎關注公衆微信號:wmyskxz 分享本身的學習 & 學習資料 & 生活 想要交流的朋友也能夠加qq羣:3382693 錢

相關文章
相關標籤/搜索