設計模式-依賴倒轉原則

依賴倒轉原則

依賴倒轉原則 (Dependency Inversion Principle, DIP) 是面向對象設計中 SOLID 原則的第五個原則。

它包含兩條核心思想:

  1. 高層模塊不應該依賴于低層模塊。兩者都應該依賴于抽象。

    • 高層模塊 (High-level modules): 通常包含復雜的業務邏輯和策略,是應用程序的核心。

    • 低層模塊 (Low-level modules): 通常提供一些基礎的、具體的實現功能,如數據庫操作、文件讀寫、網絡通信等。

  2. 抽象不應該依賴于細節。細節應該依賴于抽象。

    • 抽象 (Abstractions): 通常指接口 (Interface) 或抽象類 (Abstract Class)。

    • 細節 (Details): 通常指具體的實現類 (Concrete Class)。

簡單來說,依賴倒轉原則的核心思想是:面向接口編程,而不是面向實現編程。

為什么需要依賴倒轉?

在傳統的軟件設計中,高層模塊常常直接依賴于低層模塊。例如,一個訂單處理模塊(高層)可能直接依賴于一個 MySQL 數據庫操作模塊(低層)。

這種直接依賴的壞處:

  • 緊耦合 (Tight Coupling): 高層模塊和低層模塊緊密地綁定在一起。

  • 可測試性差 (Poor Testability): 測試高層模塊時,必須同時依賴真實的低層模塊,難以進行單元測試或模擬(Mock)低層模塊。

  • 可擴展性差 (Poor Extensibility): 如果想更換低層模塊(例如,從 MySQL 切換到 PostgreSQL,或者從文件日志切換到數據庫日志),就需要修改高層模塊的代碼。

  • 可維護性差 (Poor Maintainability): 低層模塊的改動很容易影響到高層模塊。

依賴倒轉如何解決這些問題?

依賴倒轉通過引入一個“抽象層”(通常是接口或抽象類)來解耦高層模塊和低層模塊:

  1. 高層模塊定義它所需要的接口(抽象)。

  2. 高層模塊依賴于這個接口,而不是具體的實現類。

  3. 低層模塊去實現這個接口。

這樣,高層模塊不再直接依賴于低層模塊的具體實現,而是依賴于一個雙方都認可的“契約”(接口)。依賴關系被“倒轉”了:原本是高層依賴低層,現在是低層(實現細節)依賴于高層(定義的抽象)。

一個簡單的例子:

不遵循 DIP 的設計:

// 低層模塊:郵件發送器
class EmailSender {public void sendEmail(String message) {System.out.println("Sending email: " + message);}
}
?
// 高層模塊:通知服務
class NotificationService {private EmailSender emailSender; // 直接依賴具體實現
?public NotificationService() {this.emailSender = new EmailSender(); // 高層模塊負責創建低層模塊實例}
?public void sendNotification(String message) {emailSender.sendEmail(message);}
}
?
public class Main {public static void main(String[] args) {NotificationService notificationService = new NotificationService();notificationService.sendNotification("Hello DIP!");}
}

問題:如果現在要增加短信通知,或者想在測試時使用一個假的 EmailSender,NotificationService 就必須修改。

遵循 DIP 的設計:

  1. 定義抽象(接口):

// 抽象:消息發送器接口
interface IMessageSender {void sendMessage(String message);
}
  1. 低層模塊實現抽象:

// 低層模塊:郵件發送器實現
class EmailSender implements IMessageSender {@Overridepublic void sendMessage(String message) {System.out.println("Sending email: " + message);}
}
?
// 另一個低層模塊:短信發送器實現
class SmsSender implements IMessageSender {@Overridepublic void sendMessage(String message) {System.out.println("Sending SMS: " + message);}
}
  1. 高層模塊依賴抽象:

// 高層模塊:通知服務
class NotificationService {private IMessageSender messageSender; // 依賴于抽象接口
?// 依賴通過構造函數注入 (Dependency Injection)public NotificationService(IMessageSender sender) {this.messageSender = sender;}
?public void sendNotification(String message) {messageSender.sendMessage(message);}
}
  1. 客戶端(組裝):

public class Main {public static void main(String[] args) {// 使用郵件發送IMessageSender emailSender = new EmailSender();NotificationService emailNotificationService = new NotificationService(emailSender);emailNotificationService.sendNotification("Hello via Email!");
?// 使用短信發送IMessageSender smsSender = new SmsSender();NotificationService smsNotificationService = new NotificationService(smsSender);smsNotificationService.sendNotification("Hello via SMS!");}
}

遵循 DIP 的好處:

  • 松耦合 (Loose Coupling): 高層模塊和低層模塊通過抽象解耦。NotificationService 不再關心具體的發送方式是郵件還是短信,只要它實現了 IMessageSender 接口即可。

  • 可測試性增強 (Improved Testability): 在測試 NotificationService 時,可以輕松地傳入一個模擬的 IMessageSender 實現 (Mock Object),而不需要真實的郵件或短信發送環境。

  • 可擴展性增強 (Improved Extensibility): 如果需要增加新的通知方式(如微信通知),只需創建一個新的類實現 IMessageSender 接口,然后將其注入到 NotificationService 中,而無需修改 NotificationService 本身。

  • 可維護性增強 (Improved Maintainability): 修改低層模塊的具體實現(如 EmailSender 內部的郵件發送邏輯)不會影響到高層模塊 NotificationService,只要接口契約不變。

如何實現依賴倒轉?

  • 接口 (Interfaces): 最常見的方式。

  • 抽象類 (Abstract Classes): 也可以作為抽象。

  • 依賴注入 (Dependency Injection, DI): 一種常用的實現依賴倒轉的技術模式。高層模塊不自己創建依賴對象,而是通過外部(如構造函數、setter 方法、或 DI 容器)將依賴的抽象實例“注入”進來。

總結:

依賴倒轉原則指導我們設計出更加靈活、可維護和可測試的系統。它強調了抽象的重要性,并鼓勵我們將依賴關系建立在穩定的抽象之上,而不是易變的具體實現之上。這使得系統的各個部分可以獨立地演化和替換,從而提高了軟件的整體質量。

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

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

相關文章

AI賦能引爆短劇全球化風潮,騰訊云媒體處理助力短劇平臺出海吸金

2023年,中國短劇市場以全平臺8000萬日投放、近500億規模的爆發式增長震驚行業。緊湊的內容、爽快的劇情令國內觀眾迅速愛上了幾分鐘一集的微短劇。然而在平臺內卷、監管收緊、巨頭入場等因素的沖擊下,不到兩年時間,這條賽道就已陷入紅海。但與…

開源第三方庫發展現狀

摘要:當前,開源第三方庫生態正呈現爆發式增長趨勢。GitHub 目前已托管超過 4.2 億個代碼倉庫,遠超早期統計的 1 億規模,顯示出開發者社區的活躍度持續攀升。同時,37 個主流包管理器所維護的開源組件數量可能已達到數千…

服務器開機自啟動服務

前言: 將服務器中腳本開啟自啟動執行 步驟: 1.創建一個 systemd 服務文件: /etc/systemd/system/ 目錄下創建一個新的服務文件。例如,命名為 myapp.service: sudo nano /etc/systemd/system/myapp.service2.編寫 [Unit] Descri…

采用Bright Data+n8n+AI打造自動化新聞助手:每天5分鐘實現內容日更

一、引言 在信息爆炸的時代,作為科技領域的內容創作者,我每天都要花費2-3小時手動收集行業新聞、撰寫摘要并發布到各個社群。直到我發現Bright Datan8nAI這套"黃金組合",才真正實現了從"人工搬運"到"智能自動化&qu…

ROS云課三分鐘-3D性能測試supertuxkart和游戲推薦等-國際象棋

ROS云課三分鐘-破壁篇GCompris-一小部分支持Edu應用列表-2025-CSDN博客 很多時候,有一種思維定勢,將人鎖住,人口就是囚。 口人囚~口加人等于囚-CSDN博客 如果突破,跳出問題,再看問題。 這門課程,或者這個平…

學習率及相關優化參數詳解:驅動模型高效訓練

一、引言 在模型微調的核心參數體系中,優化相關參數是決定訓練效率與模型性能的關鍵變量。它們如同精密機械的齒輪,彼此聯動、相互影響,共同調控著模型在參數空間中的搜索路徑。本文將圍繞學習率、訓練輪數、批處理大小、梯度累積和學習率調…

golang 柯里化(Currying)

使用場景:參數在語義上屬于不同組,Go 語法無法在單次調用中聲明多組可變參數,通過柯里化可以實現分步接收參數。 有的參數是在不同時間段產生,使用Currying可以讓函數記住(緩存)參數,避免應用代…

電腦革命家測試版:硬件檢測,6MB 輕量無廣告 清理垃圾 + 禁用系統更新

各位電腦小白和大神們,我跟你們說啊!有個超牛的東西叫電腦革命家測試版,這是吾愛破解論壇的開發者搞出來的免費無廣告系統工具集合,主打硬件檢測和系統優化,就像是魯大師這些軟件的平替。下面我給你們嘮嘮它的核心功能…

R 語言科研繪圖第 52 期 --- 網絡圖-分組

在發表科研論文的過程中,科研繪圖是必不可少的,一張好看的圖形會是文章很大的加分項。 為了便于使用,本系列文章介紹的所有繪圖都已收錄到了 sciRplot 項目中,獲取方式: R 語言科研繪圖模板 --- sciRplothttps://mp.…

EfficientLLM: Efficiency in Large Language Models 高效大模型

目錄 第1章:引言第2章:觀察與見解2.1 總體觀察(Overall Observations)2.2 從EfficientLLM基準中得出的新見解 第3章:背景3.1 大語言模型(LLMs)3.2 提升LLMs效率的方法3.2.1 硬件創新3.2.2 軟件優…

SFTP工具類實現文件上傳下載_

import com.jcraft.jsch.*; import com.jcraft.jsch.ChannelSftp.LsEntry;import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.*;/*** SFTP工具類*/ public class SftpFile {static Sessio…

RuoYi前后端分離框架將前端dist資源集成到Jar包中獨立部署

一、背景 .NET體系下通常采用服務端渲染(如Razor Pages)或直接包含前端資源,而Java Spring Boot項目雖支持靜態資源打包,但Vue CLI工程需要獨立的構建流程。主管要求將編譯后的Vue工程直接嵌入JAR包中方便維護,本人不推薦這樣,原因有三: 第一、Vue CLI需要npm run buil…

基于 Flink+Paimon+Hologres 搭建淘天集團湖倉一體數據鏈路

摘要:本文整理自淘天集團高級數據開發工程師朱奧老師在 Flink Forward Asia 2024 流式湖倉論壇的分享。內容主要為以下五部分: 1、項目背景 2、核心策略 3、解決方案 4、項目價值 5、未來計劃 01、項目背景 1.1 當前實時數倉架構 當前的淘天實時架構是從…

SIGCHLD信號--補充

進程一章講過用wait和waitpid函數清理僵尸進程,父進程可以阻塞等待子進程結束,也可以非阻 塞地查詢是否有子進程結束等待清理(也就是輪詢的方式)。采用第一種方式,父進程阻塞了就不 能處理自己的工作了;采用第二種方式,父進程在處理自己的工作的同時還要記得時不時地輪詢一 下,…

即插即用!全新記憶回溯策略:一種元啟發式算法的進化更新機制,含完整免費MATLAB代碼

1. 簡介 元啟發式算法的搜索域總是不斷變化,這使得難以適應多樣化的優化問題。為了克服上述問題,提出了一種稱為記憶回溯策略(MBS)的進化更新機制,包括思維階段、回憶階段和記憶階段。總體而言,MBS的采用通…

Spring AI框架快速入門

??前言:在經歷了八個里程碑式的版本之后(M1~M8),Spring AI 1.0 正式版本,終于在 2025 年 5 月 20 日正式發布,這是另一個新高度的里程碑式的版本,標志著 Spring 生態系統正式全面擁抱人工智能…

Python實戰:打造高效通訊錄管理系統

📋 編程基礎第一期《8-30》–通訊錄管理系統 📑 項目介紹 在信息化時代,高效管理個人或團隊聯系人信息變得尤為重要。本文將帶您實現一個基于Python的通訊錄管理系統,該系統采用字典數據結構和JSON文件存儲,實現了聯系…

89. Java 數字和字符串 - Math 類深入解析

文章目錄 89. Java 數字和字符串 - Math 類深入解析一、引言二、常量與基本方法2.1 Math 類常量2.2 絕對值和舍入絕對值方法舍入方法最小值和最大值 三、指數與對數方法四、三角函數方法五、總結 89. Java 數字和字符串 - Math 類深入解析 一、引言 在 Java 中,除…

STM32之SG90舵機控制(附視頻講解)

目錄 前言: 一、硬件準備與接線 1.1 硬件清單 1.2 接線 二、 SG90舵機簡介 1.1 外觀 1.2 基本參數 1.3 引腳說明 1.4 控制原理 1.5 特點 1.6 常見問題 三、 單片機簡介 四、 程序設計 4.1 定時器配置 4.2 角度控制函數 4.3 主函數調用 五、 總結 …

netstat命令Windows與Linux雙平臺

深入解析netstat命令:Windows與Linux雙平臺實戰指南 netstat(Network Statistics)是網絡診斷中最經典的工具之一,能夠幫助用戶查看網絡連接、端口監聽狀態、路由表等信息。然而,Windows和Linux系統下的netstat在參數和輸出格式上存在差異,容易讓人混淆。本文將詳細對比兩…