依賴倒置原則 Dependency Inversion Principle - DIP

基本知識

1.依賴倒置原則(DIP)是面向對象設計(OOD)中的五個基本原則之一,通常被稱為 SOLID 原則中的 D
2.核心思想:
高層模塊不應該依賴低層模塊,兩者都應該依賴抽象。 (High-level modules should not depend on low-level modules. Both should depend on abstractions.)
抽象不應該依賴于細節,細節應該依賴于抽象。 (Abstractions should not depend on details. Details should depend on abstractions.)

簡單來說,DIP 提倡的是在軟件設計中,我們應該讓依賴關系指向抽象(如接口或抽象類),而不是具體的實現(如具體的類)。這顛覆了傳統上高層模塊直接依賴低層模塊的“自上而下”的依賴關系,從而實現了“倒置”的效果。

3.依賴:
“依賴”指的是一個軟件構件(例如一個類、一個模塊、一個組件或一個系統)需要另一個構件才能正常工作或完成其功能。

常見四種依賴:
1.編譯時依賴:個類 A 的代碼中引用了類 B 的成員變量、方法、常量、類型定義(如作為參數類型、返回類型、局部變量類型、父類或接口)

class B {public void doSomething() { /* ... */ }
}class A {private B bInstance; // A 依賴 B 作為成員變量public void methodA(B param) { // A 依賴 B 作為方法參數B localB = new B(); // A 依賴 B 作為局部變量和構造函數param.doSomething(); // A 依賴 B 的方法}
}

2.運行時依賴:
即使在編譯時沒有直接引用,但在程序運行時,構件 A 可能需要構件 B 提供的服務或數據。例如:反射時候A需要使用B的方法,雖然編譯A的時候不需要B,但是運行時候B需要存在。又例如:配置文件
3.直接依賴和間接依賴:構件A直接引用構件B稱為直接依賴,而構件A依賴構件B,構件B依賴構件C,此為間接依賴
4.接口依賴和實現依賴:
接口依賴 Interface Dependency:一個構件依賴另一個構件的接口
實現依賴 Implementation Dependency:一個構件依賴另一個構件的具體實現類。這種會造成緊耦合,DIP正是為了避免這種依賴。
例子:

class A { private List<String> list; }:A 依賴于 List 接口(接口依賴)。
class A { private ArrayList<String> list; }:A 依賴于 ArrayList 具體實現類(實現依賴)

具體實例

1.沒有實現DIP的情況

即傳統的高層模塊直接依賴低層模塊

public class Bike {public void take(){System.out.println("騎自行車");}
}
public class Car {public void run(){System.out.println("開車");}
}
public class Human {private Bike bike;private Car car;public Human(){this.bike=new Bike();this.car=new Car();}public void driverCar(){this.car.run();}public void takeBike(){this.bike.take();}
}
public class Test {public static void main(String[] args) {Human human=new Human();human.driverCar();human.takeBike();}
}

高層模塊Human類依賴Bike類和Car類,在human類中聲明了兩個成員變量,分別時Bike類和Car類,并在構造方法中實例化了Bike類和Car,這些都明顯違反了DIP,有強耦合情況。
那么違反DIP的問題:
1.難以擴展,比如我們后面如果引入另外的交通工具,比如MotoCycle,或者Bus,或者Subway,那么我們必須修改Human類,添加對應的成員變量,然后進行實例化,再寫新的方法。
2.難以測試,如果想單獨測試 Human 類(例如,測試 driverCar 方法的邏輯,但不真正涉及到實際的汽車運行),很難對 Bike 和 Car 進行模擬或替換,因為它們在 Human 內部被硬編碼創建了。

2.使用DIP

public interface ITransportation {void take();
}
public class Bike implements ITransportation{@Overridepublic void take() {System.out.println("騎自行車");}
}
public class Bus implements ITransportation{@Overridepublic void take() {System.out.println("坐汽車");}
}
public class Car implements ITransportation{@Overridepublic void take() {System.out.println("開車");}
}
public class Human {private ITransportation transportation;//依賴抽象接口/*通過構造方法注入依賴*/public Human(ITransportation iTransportation){this.transportation=iTransportation;}public void take(){transportation.take();}}
ublic class Test {public static void main(String[] args) {Bike bike = new Bike();Human human  = new Human(bike);human.take();Bus bus  =new Bus();Human human1=new Human(bus);human1.take();Car car = new Car();Human human2 = new Human(car);human2.take();}
}

這段代碼中,我們使用ITransportation 接口。這是一個抽象,定義了“交通工具”應該具備的通用行為 take()。

低層模塊依賴抽象:Bike, Bus, Car 類。這三個類是具體的低層實現(細節)。
它們都實現了 ITransportation 接口。這意味著它們現在依賴于這個抽象來提供其具體功能。它們不是獨立的,而是受 ITransportation 接口定義的約束。

高層模塊依賴抽象:Human 類
Human 類是高層模塊,它需要使用交通工具。
private ITransportation transportation;:Human 類內部聲明了一個 ITransportation 類型的成員變量,而不是具體的 Bike、Bus 或 Car 類型。這表明 Human 依賴于抽象。

依賴注入(通過構造函數):

public Human(ITransportation iTransportation){this.transportation=iTransportation;
}

Human 不再在自己的內部(比如構造函數中)實例化具體的交通工具。相反,它通過構造函數接收一個已經創建好的 ITransportation 實例。這是一種經典的依賴注入方式。Human 不知道它會具體操作的是自行車、巴士還是汽車,它只知道如何與任何實現了 ITransportation 接口的對象進行交互。

而“細節應該依賴于抽象” 這句話,在代碼例子中,正是對應 Car, Bus, Bike 實現 ITransportation 接口的情況。

細節(Details):在這里指的是 Car、Bus 和 Bike 這些具體的實現類。它們包含了實現“交通工具”功能的具體細節,比如 System.out.println(“開車”) 或 System.out.println(“騎自行車”)。

抽象(Abstractions):在這里指的是 ITransportation 接口。它定義了一個通用的契約:任何實現了它的類都必須提供一個 take() 方法。

“依賴于”:通過 implements 關鍵字,Car、Bus 和 Bike 在編譯時就被強制要求遵循 ITransportation 接口的規范。它們的實現必須符合接口的定義。如果它們不實現 take() 方法,或者方法簽名不正確,編譯器就會報錯。

倒置”的效果:

在舊的代碼中:Human (高層) -> Bike (低層), Human (高層) -> Car (低層)。

在這段代碼中:

Human (高層) -> ITransportation (抽象)。

Bike, Bus, Car (低層) -> ITransportation (抽象)。

依賴關系不再是高層指向具體的低層,而是高層和低層都指向了同一個抽象。這正是依賴關系的“倒置”。

回顧下

回顧最初的兩句話:
高層模塊不應該依賴低層模塊,兩者都應該依賴抽象。
抽象不應該依賴于細節,細節應該依賴于抽象。

顯然,我們看到Human這個高層模塊里面有抽象接口的成員變量,故高層模塊依賴抽象。對于低層模塊Car,Bus,Bike。我們可以看到“一個類 A 的代碼中引用了類 B 的…接口”中對應上:A是Car類,B是ITransportation 接口。即A引用B類型作為接口
Car 類的代碼中,明確地使用了 ITransportation 這個接口類型:

public class Car implements ITransportation

所以低層模塊也是依賴抽象的。這種依賴是通過 implements 關鍵字在編譯時建立的,它強制 Car 去遵守 ITransportation 定義的契約

符合 DIP 帶來的好處:

降低耦合度: Human 類不再知道任何關于 Bike、Bus 或 Car 具體實現的信息。它只關心 ITransportation 接口。

提高可擴展性: 如果將來需要增加新的交通工具(例如 Train, Airplane),只需要創建新的類實現 ITransportation 接口即可,無需修改 Human 類的任何代碼。這完全符合開閉原則。

提高可測試性: 在測試 Human 類時,可以很容易地創建 ITransportation 接口的模擬(Mock)實現,以便隔離測試 Human 自身的邏輯,而無需依賴真實的交通工具。

提高可維護性: 交通工具的實現細節變化不會影響到 Human 類。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/90948.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/90948.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/90948.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

原生input添加刪除圖標類似vue里面移入顯示刪除[jquery]

<input type"text" id"servicer-search" class"form-control" autocomplete"off" />上面是剛開始的input <div class"servicer-search-box"><input type"text" id"servicer-search" cla…

整理分享 | Photoshop 2025 (v26.5) 安裝記錄

導語&#xff1a; 最近整理資源時&#xff0c;發現有朋友在找新版 Photoshop。正好手邊有 Photoshop 2025年7月的版本&#xff08;v26.5&#xff09;&#xff0c;就記錄下來分享給大家&#xff0c;供有需要的朋友參考。關于這個版本&#xff1a;這個 Photoshop v26.5 安裝包&am…

【Redis】Redis 數據存儲原理和結構

一、Redis 存儲結構 1.1 KV結構 Redis 本質上是一個 Key-Value&#xff08;鍵值對&#xff0c;KV&#xff09;數據庫&#xff0c;在它豐富多樣的數據結構底層&#xff0c;都基于一種統一的鍵值對存儲結構來進行數據的管理和操作 Redis 使用一個全局的哈希表來管理所有的鍵值對…

【RAG優化】深度剖析OCR錯誤,從根源修復RAG應用的識別問題

1. 引言:OCR——RAG系統中的關鍵問題 當我們將一個包含掃描頁面的PDF或一張報告截圖扔給RAG系統時,我們期望它能“讀懂”里面的內容。這個“讀懂”的第一步,就是OCR。然而,OCR過程并非100%準確,它受到圖像質量、文字布局、字體、語言等多種因素的影響。 一個看似微不足道…

【第六節】方法與事件處理器

方法與事件處理器 方法處理器 可以用 v-on 指令監聽 DOM 事件: <div id="example"> <button v-on:click="greet">Greet</button></div>綁定一個單擊事件處理器到一個方法 greet 。下面在 Vue 實例中定義這個方法 var vm=new V…

大語言模型Claude 4簡介

Anthropic公司成立于2021年&#xff0c;由一群OpenAI前員工組成。他們最新發布的大語言模型(Large Language Model, LLM) Claude 4系列包括兩個版本&#xff1a;Claude Opus 4和Claude Sonnet 4&#xff1a;(1).Claude Sonnet 4&#xff1a;是Claude Sonnet 3.7的升級&#xff…

國產化PDF處理控件Spire.PDF教程:Python 將 PDF 轉換為 Markdown (含批量轉換示例)

PDF 是數字文檔管理的普遍格式&#xff0c;但其固定布局特性限制了在需要靈活編輯、更新或現代工作流集成場景下的應用。相比之下&#xff0c;Markdown&#xff08;.md&#xff09;語法輕量、易讀&#xff0c;非常適合網頁發布、文檔編寫和版本控制。 E-iceblue旗下Spire系列產…

PDF轉Markdown - Python 實現方案與代碼

PDF作為廣泛使用的文檔格式&#xff0c;轉換為輕量級標記語言Markdown后&#xff0c;可無縫集成到技術文檔、博客平臺和版本控制系統中&#xff0c;提高內容的可編輯性和可訪問性。本文將詳細介紹如何使用國產Spire.PDF for Python 庫將 PDF 文檔轉換為 Markdown 格式。 技術優…

深度解析 inaSpeechSegmenter:高效音頻語音分割與檢測開源工具

項目簡介 inaSpeechSegmenter 是法國國家視聽研究院(INA)開源的音頻分割與檢測工具,專為廣播、播客、采訪、影視等多媒體內容的自動化處理設計。它能夠高效地將長音頻自動分割為語音、音樂、噪聲、靜音等片段,并支持性別檢測(男聲/女聲),為后續的語音識別、內容檢索、轉…

VirtualBox安裝Ubuntu 22.04后終端無法打開的解決方案

問題現象在VirtualBox中使用"快速安裝"模式安裝Ubuntu 22.04后圖形終端&#xff08;gnome-terminal&#xff09;無法通過圖標或快捷鍵(CtrlAltT)啟動系統其他功能正常根本原因語言環境(Locale)配置異常導致&#xff1a;快速安裝模式可能跳過Locale生成步驟gnome-term…

java磁盤操作與IO流(序列化、Properties類)

目錄 一、磁盤操作 1、File類&#xff1a; &#xff08;1&#xff09;創建File對象&#xff1a; &#xff08;2&#xff09;獲取文件信息&#xff1a; &#xff08;3&#xff09;判斷文件 &#xff08;4&#xff09;刪除文件 &#xff08;5&#xff09;創建文件&#xff…

【WPF】WPF Prism 開發經驗總結:菜單命令刪除項時報 InvalidCastException 的問題分析與解決

WPF Prism 開發經驗總結&#xff1a;菜單命令刪除項時報 InvalidCastException 的問題分析與解決 在 WPF Prism 項目中使用 ContextMenu 執行刪除操作時&#xff0c;遇到一個令人疑惑的問題&#xff1a;命令綁定本身沒有問題&#xff0c;但點擊“刪除”菜單后&#xff0c;程序拋…

《WebGL打造高性能3D粒子特效系統:從0到1的技術探秘》

在游戲里,爆炸時四濺的火花、魔法釋放時閃爍的光暈;在可視化項目中,數據流動時呈現的璀璨光河,這些令人驚嘆的效果,背后離不開強大的技術支撐。而WebGL,作為在瀏覽器端實現硬件加速3D圖形渲染的技術,為我們開啟了構建高性能3D粒子特效系統的大門。 WebGL的渲染管線是整…

全國計算機等級考試二級題庫【C語言】:程序填空題型——結構體 自制答案詳解合輯

二級C語言程序填空題型簡介 1、/**********found**********/緊跟的下面一行的程序設空,一般為3個空; 2、常見錯誤: (1) (2) 3、做題推薦步驟: (1) (2) ---------------一、結構體--------------- 2、題目要求【結構體】 程序通過定義學生結構體變量,存儲了學生…

人工智能與城市:城市生活的集成智能

1. 智慧城市的核心價值&#xff1a;從 “硬件堆砌” 到 “智能協同”1.1 傳統城市的治理困境全球 55% 的人口居住在城市&#xff0c;到 2050 年這一比例將升至 68%。傳統城市管理面臨多重挑戰&#xff1a;資源分配失衡&#xff1a;早晚高峰主干道擁堵率達 80%&#xff0c;而支線…

Linux下掛載磁盤報superblock錯誤

Linux下掛載磁盤報superblock錯誤背景問題現象1、使用fdisk查詢設備文件信息2、掛載磁盤&#xff0c;報出fs type錯誤解決辦法1、使用e2fsk命令檢查整個磁盤2、resize2fs 命令調整文件系統塊大小和物理磁盤塊大小3、掛載磁盤&#xff0c;確認修復結果問題思考1、rclone命令做數…

Http證書體系及證書加密流程(通信流程)

一、HTTPS 證書體系&#xff1a;信任的基石 HTTPS 證書體系是保障網絡通信安全的核心機制&#xff0c;其本質是一套基于公鑰基礎設施&#xff08;PKI&#xff0c;Public Key Infrastructure&#xff09; 的信任體系&#xff0c;通過數字證書實現通信雙方的身份驗證和數據加密&…

【分布式架構】學習路徑概述:了解分布式系統的核心問題、解決方案與實戰說明

文章目錄零、前言一、分布式系統理論1、 分布式系統的一致性問題1.1、一致性問題理論&#xff08;CAP/BASE&#xff09;1.2、 一致性協議與算法&#xff08;Paxos/Raft&#xff09;&#xff1a;選主、分布式鎖1.3、 分布式事務(2PC\3PC\TCC)&#xff1a;服務一致性保障與性能2、…

C# 密封類_密封方法 (seadled 關鍵字)

C#允許將類聲明為密封類&#xff0c;密封類不能被繼承在什么場景用&#xff1f;答&#xff1a;防止重寫某些類導致代碼混亂密封類seadled 聲明密封類的關鍵字//seadled 聲明密封類的關鍵字 //密封類不能被繼承 sealed class Class1 {public int age;public string name;publi…

深度學習(魚書)day04--手寫數字識別項目實戰

深度學習&#xff08;魚書&#xff09;day04–手寫數字識別項目實戰 魚書的相關源代碼下載&#xff1a; 點擊鏈接&#xff1a;http://www.ituring.com.cn/book/1921 點擊“隨書下載” 第三項就是源代碼&#xff1a; 解壓后&#xff0c;在pycharm&#xff08;或其它IDE&#…