依賴倒置原則

来源:https://www.cnblogs.com/liebrother/archive/2019/01/01/10205927.html
-Advertisement-
Play Games

個人博客原文: "依賴倒置原則" 設計模式六大原則之三:依賴倒置原則。 簡介 姓名 :依賴倒置原則 英文名 :Dependence Inversion Principle 價值觀 :大男子主義的典型代表,什麼都得通過老大或者老爸同意 伴侶 :一定是個溫柔體貼的女子 個人介紹 : 1. High le ...


個人博客原文:
依賴倒置原則

雪

設計模式六大原則之三:依賴倒置原則。

簡介

姓名 :依賴倒置原則

英文名 :Dependence Inversion Principle

價值觀 :大男子主義的典型代表,什麼都得通過老大或者老爸同意

伴侶 :一定是個溫柔體貼的女子

個人介紹

  1. High level modules should not depend upon low level modules.Both should depend upon abstractions. 高層模塊不應該依賴低層模塊,兩者都應該依賴其抽象(模塊間的依賴通過抽象發生,實現類之間不發生直接的依賴關係,其依賴關係是通過介面或抽象類產生的)
  2. Abstractions should not depend upon details. 抽象不應該依賴細節(介面或抽象類不依賴於實現類)
  3. Details should depend upon abstractions. 細節應該依賴抽象(實現類依賴介面或抽象類)

給大家講個故事,我胡亂想的,如有雷同,肯定是英雄所見略同。那必須交個朋友。

一個小村裡,有兩家飯館,雖然掛著不同的牌子,挨在一起,但是老闆確是表兄弟。這兩兄弟摳得很,為了節省成本,密謀了一個想法:在兩家飯館誰家忙的時候,可以讓不忙的那家的員工過去支援一下。這樣子,本來每家飯館都需要 2 個洗碗工,總共需要 4 個,他們就只招了 3 個,省了 1 個洗碗工的成本,當然不止洗碗工,還有服務員等等。兩兄弟約定了規則:

  1. A 飯館需要支援的時候,B 飯館老闆,讓 B 飯館老闆選哪個員工去支援,不能直接讓 A 飯館的員工直接找 B 飯館的員工去幫忙,但可以讓 A 飯館員工找 B飯館老闆告知需要支援。
  2. 雖然老闆權利大,但是也不能說 A 飯館老闆直接叫 B 飯館的員工去幫忙。
  3. 員工沒有真實的老闆,今天為 A 飯館工作就是 A 飯館的員工,沒有跟定哪個老闆。

大概通過這個小故事,描述了依賴倒置原則的基本內容。

代碼複原

下麵通過代碼來模擬這個故事。

錯誤的示範

這個錯誤的示範將就看哈,可能有些問題沒描述清楚。

老闆和員工抽象

abstract class Boss {

    abstract void support();

    abstract void askHelp(Boss boss);
}

abstract class Staff {

    private String name;

    abstract void service();

    abstract void askHelp(Boss boss);

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

老闆具體類

class BossA extends Boss {

    private StaffA staffA;

    public BossA(StaffA staffA) {
        this.staffA = staffA;
    }

    @Override
    void support() {
        staffA.service();
    }

    @Override
    void askHelp(Boss boss) {
        boss.support();
    }

}

class BossB extends Boss {

    private StaffB staffB;

    public BossB(StaffB staffB) {
        this.staffB = staffB;
    }

    @Override
    void support() {
        staffB.service();
    }

    @Override
    void askHelp(Boss boss) {
        boss.support();
    }
}

員工具體類

class StaffA extends Staff {

    public StaffA(String name) {
        this.setName(name);
    }

    @Override
    void service() {
        System.out.println(this.getName() + "提供服務");
    }

    @Override
    void askHelp(Boss boss) {
        boss.support();
    }
}

class StaffB extends Staff {

    public StaffB(String name) {
        this.setName(name);
    }

    @Override
    void service() {
        System.out.println(this.getName() + "提供服務");
    }

    @Override
    void askHelp(Boss boss) {
        boss.support();
    }
}

測試代碼

/** 初始化老闆和員工 */
StaffA staffA = new StaffA("A 員工");
StaffB staffB = new StaffB(" B 員工");
Boss bossA = new BossA(staffA);
Boss bossB = new BossB(staffB);

/** A 老闆向 B 老闆求支援 */
bossA.askHelp(bossB); // 列印出:B 員工提供服務

/** B 員工向 A 老闆求支援 */
staffB.askHelp(bossA); // 列印出:A 員工提供服務

好像看起來實現了要求了,但是其實這段代碼沒有按照上面的 3 點規則編寫,破壞了第 3 點規則,老闆們的員工沒有用員工的抽象類,破壞了細節依賴抽象這一點。設想一下,假如現在 A 老闆把 A 員工辭退了,重新招了個 C 員工,那麼怎麼實現呢?是不是需要再新增一個 StaffC 類,然後再修改 BossA 類代碼,把 StaffA 換成 StaffC。這樣超級麻煩,在平時寫項目中要時刻考慮這一點:在具體實現類使用其他類,是不是可以用其抽象類?

代碼:

DIPErrorTest.java

正確的示範

看了上面那個憋屈的代碼,再來看下麵簡潔的代碼,才會發現依賴倒置原則是多麼強大。

老闆和員工抽象類

abstract class Boss2 {

    private Staff2 staff;

    public Boss2(Staff2 staff) {
        this.staff = staff;
    }

    abstract void support();

    abstract void askHelp(Boss2 boss);

    public void setStaff(Staff2 staff) {
        this.staff = staff;
    }

    public Staff2 getStaff() {
        return staff;
    }
}

abstract class Staff2 {

    private String name;

    abstract void service();

    abstract void askHelp(Boss2 boss);

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

老闆類

class BossImpl extends Boss2 {

    public BossImpl(Staff2 staff) {
        super(staff);
    }

    @Override
    void support() {
        this.getStaff().service();
    }

    @Override
    void askHelp(Boss2 boss) {
        boss.support();
    }
}

員工類

class StaffImpl extends Staff2{

    public StaffImpl(String name) {
        this.setName(name);
    }

    @Override
    void service() {
        System.out.println(this.getName() + "提供服務");
    }

    @Override
    void askHelp(Boss2 boss) {
        boss.support();
    }
}

測試類

/** 正確示範 */
Staff2 staffA2 = new StaffImpl("A 員工");
Staff2 staffB2 = new StaffImpl("B 員工");
Boss2 bossA2 = new BossImpl(staffA2);
Boss2 bossB2 = new BossImpl(staffB2);

/** A 老闆向 B 老闆求支援 */
bossA2.askHelp(bossB2); // 列印出:B 員工提供服務

/** B 員工向 A 老闆求支援 */
staffB2.askHelp(bossA2); // 列印出:A 員工提供服務

/** A 老闆辭退了 A 員工,換成了 C 員工 */
Staff2 staffC2 = new StaffImpl("C 員工");
bossA2.setStaff(staffC2);

/** B 員工向 A 老闆求支援 */
staffB2.askHelp(bossA2); // 列印出:C 員工提供服務

這代碼相比上面錯誤的示範,簡潔了很多,實現的功能卻更靈活,這就是依賴倒置原則強大的地方,它可以將類的耦合性降低,提供靈活的處理。

代碼:

DIPRightTest.java

最佳實踐

  1. 變數的錶面類型儘量是介面或者是抽象類
  2. 任何類都不應該從具體類派生
  3. 儘量不要覆寫基類的方法
  4. 結合里氏替換原則使用
    (來自《設計模式之禪》)

總結

總的來說,要實現依賴倒置原則,要有『面向介面編程』這個思維,掌握好這個思維後,就可以很好的運用依賴倒置原則。

參考資料:《大話設計模式》、《Java設計模式》、《設計模式之禪》、《研磨設計模式》、《Head First 設計模式》

歡迎大家關註公眾號 LieBrother,一起學習、進步!

公眾號


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 原文地址:https://medium.com/javascript scene/rethinking unit test assertions 55f59358253f 作者: "Eric Elliott" 「斷言」是編程術語,表示為一些布爾表達式,程式員相信在程式中的某個特定點該表達式值為真,可 ...
  • 什麼是客戶端? 客戶端(Client)或稱為用戶端,是指與伺服器相對應,為客戶提供本地服務的程式。 第一天學習知識的總結 <!DOCTYPE>是什麼意思? <!DOCTYPE>位於HTML第一行,在<html>標簽之前,用來告訴瀏覽器用什麼文檔標準,解析這個文檔是標準模式還是相容模式。如果該標簽不存 ...
  • 目標效果:點擊搜索框,搜索框內提示信息消失,可輸入搜索信息,點擊搜索框外搜索框如果沒提示信息或者為空時,顯示搜索框提示信息,如果有搜索信息,顯示搜索信息。 代碼如下: ...
  • 前言 業餘時間充實自我,入手學習瞭解一下傳說中純事件驅動/非阻塞的js架構 --nodejs 好記性不如爛筆頭,本系列隨筆用於整理記錄學習nodejs過程中的心得 目錄 1. nodejs簡介 nodejs基於Google開源js引擎V8開發,從2009年誕生起就獲得強烈關註,是一套用來編寫高性能網 ...
  • H5新增的幾個容器塊元素:1、header:用戶表示頁面或某個區域的頭部2、nav:用於表示導航欄3、aside:用於表示跟周圍主題相關的附加信息4、article:用於表示一個整體的一部分主題 5、section:表示右邊的側邊欄6、footer:用於表示頁面或某個區域的腳註 現在這裡來教大家怎麼 ...
  • 用戶註冊時將加密後的密碼發送給後端存儲 當登陸的時候,再將加密後的密碼和資料庫中加密的密碼相匹配。 npm: "https://www.npmjs.com/package/crypto browserify" 此加密無須解密。 安裝: 在需要用到的文件中通過 import 引入。 調用 create ...
  • 1.正則 Regexp 正則 就是一條規則,用於檢驗字元串的格式,目標就是字元串 只要是表單提交的數據都是字元串 2.正則的定義 1.var reg=new RegExp() 2.var reg=/格式/ 3.正則的方法 正則就兩大功能:一個匹配,一個是捕獲,匹配成功就是true,第二個捕獲,如果有 ...
  • 這裡是埠衝突問題: 可以做一下幾個解決方案: 第一:更好Tomcat/conf/server.xml文件裡面的制定8080埠號,更改為你能記住的埠數; 第二:找到衝突的埠的進程,殺死這個進程,然後重啟start.bat. ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...