談一談依賴倒置原則

為獲得良好的閱讀體驗,請訪問原文:

一、概念

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

抽象不應該依賴細節,細節應該依賴抽象。通過依賴倒置,可以減少類與類之間的耦合性,提高系統的穩定性,提高代碼的可讀性和可維護性,並且能夠降低修改程序所造成的風險。

二、為什麼

先來看一個例子

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

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 課程的學習,他們三節課本質上行為都是一樣的,如果我們任由這樣行為近乎一樣的代碼在我們的類裏面肆意擴展的話,很快我們的類就會變得臃腫不堪,等到我們意識到不得不重構這個類以緩解這樣的情況的時候,或許成本已經變得高得可怕了。

原因三:降低耦合

《資本論》中有這樣一段描述:

在商品經濟的萌芽時期,出現了物物交換。假設你要買一個 iPhone,賣 iPhone 的老闆讓你拿一頭豬跟他換,可是你並沒有養豬,你只會編程。所以你找到一位養豬戶,說給他做一個養豬的 APP 來換他一頭豬,他說換豬可以,但是得用一條金項鏈來換…

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

三、怎麼做

我們現在的代碼是上層直接依賴低層實現,現在我們需要定義一個抽象的 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. – 那些年搞不懂的高深術語——依賴倒置•控制反轉•依賴注入•面向接口編程
  2. 《Spring 5 核心原理 與 30 個類手寫實戰》 – 譚勇德 著

按照慣例黏一個尾巴:

歡迎轉載,轉載請註明出處!
獨立域名博客:wmyskxz.com
簡書ID:
github:
歡迎關注公眾微信號:wmyskxz
分享自己的學習 & 學習資料 & 生活
想要交流的朋友也可以加qq群:3382693

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

※專營大陸快遞台灣服務

台灣快遞大陸的貨運公司有哪些呢?