SOLID - 依赖倒置原则(Dependency Inversion Principle)
SOLID - 依赖倒置原则(Dependency Inversion Principle)

定义
依赖倒置原则(Dependency Inversion Principle,DIP)是面向对象设计中的五大基本原则之一,通常缩写为SOLID中的D。DIP由Robert C. Martin提出,其核心思想是:
Depend upon abstractions, not concretions.
依赖倒置原则主要体现在以下两个方面:
- 高层模块不应依赖于低层模块,两者都应该依赖于抽象。
- 抽象不应该依赖于细节,细节应该依赖于抽象。
通过遵循依赖倒置原则,可以使得系统架构更加稳定、灵活和易于维护。
使用情境
依赖倒置原则主要应用在以下情境中:
- 模块化设计:在模块化设计中,通过依赖抽象,提升模块的重用性和灵活性。
- 插件架构:在插件架构中,不同插件通过抽象接口进行通信,实现松耦合。
- 依赖注入:通过依赖注入(Dependency Injection,DI)实现依赖倒置,有助于提高代码的可测试性和可维护性。
示例
以下是一个不遵循依赖倒置原则的示例:
不遵循依赖倒置原则的设计
class MySqlDatabase {public void connect() {// MySQL数据库连接逻辑}
}class DataService {private MySqlDatabase mySqlDatabase;public DataService() {this.mySqlDatabase = new MySqlDatabase();}public void getData() {mySqlDatabase.connect();// 获取数据逻辑}
}
在这个设计中,DataService类直接依赖于具体的MySqlDatabase类。当需要更换数据库实现时,必须修改DataService类的代码。
遵循依赖倒置原则的设计
为了消除这种依赖,可以引入一个抽象接口:
interface Database {void connect();
}class MySqlDatabase implements Database {public void connect() {// MySQL数据库连接逻辑}
}class OracleDatabase implements Database {public void connect() {// Oracle数据库连接逻辑}
}class DataService {private Database database;public DataService(Database database) {this.database = database;}public void getData() {database.connect();// 获取数据逻辑}
}
在这个设计中,DataService类通过依赖注入获得一个Database接口的实现,这样就实现了对抽象的依赖,而不是具体实现。这个设计使得DataService类不再依赖于特定的数据库实现,从而提升了系统的灵活性。
在Spring Boot中的应用

在Spring Boot中,DIP作为Spring Framework核心理念的一个重要组成部分,得到了广泛应用。DIP主要通过依赖注入(Dependency Injection, DI)实现,使得高层模块不会直接依赖于低层模块,而是依赖于抽象接口,而低层模块也实现这些抽象接口,从而实现系统的松耦合和高扩展性。
以下通过一个具体示例展示如何在Spring Boot中使用依赖倒置原则。
示例:订单服务与支付服务
假设我们需要实现一个订单服务(OrderService),其中包含一个支付服务(PaymentService)。为了使系统更具灵活性和可扩展性,我们将使用依赖倒置原则设计这些服务。
1. 定义抽象接口
首先,我们定义一个支付服务接口PaymentService,并定义一个实现类PayPalService和StripeService。
// src/main/java/com/example/demo/service/PaymentService.java
package com.example.demo.service;public interface PaymentService {void processPayment(double amount);
}// src/main/java/com/example/demo/service/impl/PayPalService.java
package com.example.demo.service.impl;
import com.example.demo.service.PaymentService;
import org.springframework.stereotype.Service;@Service
public class PayPalService implements PaymentService {@Overridepublic void processPayment(double amount) {System.out.println("Processing payment of $" + amount + " through PayPal");}
}// src/main/java/com/example/demo/service/impl/StripeService.java
package com.example.demo.service.impl;
import com.example.demo.service.PaymentService;
import org.springframework.stereotype.Service;@Service
public class StripeService implements PaymentService {@Overridepublic void processPayment(double amount) {System.out.println("Processing payment of $" + amount + " through Stripe");}
}
2. 注入依赖
然后,我们定义订单服务OrderService,并通过构造器注入依赖的支付服务PaymentService。
// src/main/java/com/example/demo/service/OrderService.java
package com.example.demo.service;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class OrderService {private final PaymentService paymentService;@Autowiredpublic OrderService(PaymentService paymentService) {this.paymentService = paymentService;}public void createOrder(double amount) {// 订单创建逻辑System.out.println("Creating order for amount: $" + amount);paymentService.processPayment(amount);}
}
3. 配置类
为了更好地控制依赖,可以通过配置类来指定使用的具体实现。在此示例中,我们可以使用Spring的配置类来让Spring容器管理我们选择的支付服务。
// src/main/java/com/example/demo/config/AppConfig.java
package com.example.demo.config;import com.example.demo.service.PaymentService;
import com.example.demo.service.impl.PayPalService;
import com.example.demo.service.impl.StripeService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class AppConfig {@Beanpublic PaymentService paymentService() {// 在这里选择具体实现类,可以通过条件选择不同实现return new PayPalService();// 或者// return new StripeService();}
}
4. 启动类
接下来,我们编写Spring Boot启动类并运行应用。
// src/main/java/com/example/demo/DemoApplication.java
package com.example.demo;import com.example.demo.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class DemoApplication implements CommandLineRunner {@Autowiredprivate OrderService orderService;public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}@Overridepublic void run(String... args) throws Exception {orderService.createOrder(100.0);}
}
5. 运行结果
运行应用后,可以看到如下输出(如果配置为使用PayPalService):
Creating order for amount: $100.0
Processing payment of $100.0 through PayPal
结论
依赖倒置原则(DIP)是创建高效、灵活、可维护系统的重要原则之一。依赖倒置原则通过让高层模块依赖抽象接口,而不是具体实现,使得系统更具灵活性和可扩展性。Spring Boot通过依赖注入(Dependency Injection)和配置类(Configuration)提供了优雅的方式来实现这一原则,使得开发更加简便和高效。
关于SOLID设计原则的总体描述,请参考:软件设计还是要SOLID!
相关文章:
SOLID - 依赖倒置原则(Dependency Inversion Principle)
SOLID - 依赖倒置原则(Dependency Inversion Principle) 定义 依赖倒置原则(Dependency Inversion Principle,DIP)是面向对象设计中的五大基本原则之一,通常缩写为SOLID中的D。DIP由Robert C. Martin提出&…...
【.NET 8 实战--孢子记账--从单体到微服务】--需求拆分与规划
在上一篇文章中我们收集了需求,并对需求进行了简单的分析和规划,但是对于开发人员来说,上一篇文章的需求还不够详细,并且没有形成计划。因此本篇文章将带领大家来拆分需求并规划开发里程碑。 一、详细需求列表 项目组进行了多次…...
在macOS的多任务处理环境中,如何平衡应用的性能与用户体验?这是否是一个复杂的优化问题?如何优化用户体验|多任务处理|用户体验|应用设计
目录 一 多任务处理与应用性能 1. macOS中的多任务处理机制 2. 性能优化的基本策略 二 用户体验的关键要素 1. 响应速度 2. 界面友好性 3. 功能的直观性 三 平衡性能与用户体验的策略 1. 资源管理 2. 优化数据加载 3. 使用合适的线程模型 4. 实时监测和调整 四 使…...
Vscode配置CC++编程环境的使用体验优化和补充说明
文章目录 快速编译运行👺code runner插件方案Code Runner Configuration 直接配置 相关指令和快捷键默认task配置和取消默认 配置文件补充介绍(可选 推荐阅读)😊使用vscode预置变量和环境变量环境变量的使用使用环境变量的好处环境变量可能引起的问题 检…...
十个方法杜绝CAD图纸泄密风险!2024年图纸防泄密指南!「必看」
随着信息技术的发展,CAD图纸的应用日益普遍,然而随之而来的图纸泄密风险也愈加严重。企业在提升效率的同时,更需重视信息安全。为此,本文将介绍十个有效的方法,帮助企业杜绝CAD图纸泄密风险,保障商业机密。…...
技术干货|HyperMesh CFD功能详解:虚拟风洞 Part 1
虚拟风洞VWT 从2023版本开始,虚拟风洞VWT(Virtual Wind Tunnel)模块合并到HyperMesh CFD中。 用户在VWT模块中完成LBM求解器ultraFluidX的前处理设置,导出参数文件XML和模型文件STL,并在GPU服务器上提交计算。 VWT目前…...
022集——统计多条线的总长度(CAD—C#二次开发入门)
如下图所示,选择多条线并统计长度: c#中不包含直接获取curve曲线长度 属性,需用如下方法:curve.GetDistanceAtParameter(item.EndParam) 附部分代码如下: using Autodesk.AutoCAD.ApplicationServices; using Autode…...
大模型重要技术系列三:高效推理
接上一篇高效训练,这一篇汇总下高效推理的方法。高效推理的两个主要优化目标是低延迟(快速得到推理结果)和高吞吐量(能同时处理很多请求),同时还要尽可能地少用资源(算力、存储、网络带宽&#…...
Android 刘海屏适配指南
如果您不希望您的内容与刘海区域重叠, 以确保您的内容不会与状态栏及 导航栏。如果您要呈现在刘海区域中,请使用 WindowInsetsCompat.getDisplayCutout() 检索 DisplayCutout 对象 包含每个刘海屏的安全边衬区和边界框。借助这些 API 您需要检查视频内容…...
微信小程序服务通知
项目中用到了小程序的服务消息通知,通知订单状态信息,下边就是整理的一下代码,放到项目中,把项目的小程序appid和小程序的secret写进去,直接运行即可 提前申请好小程序服务信息通知短信模板,代码需要用到模…...
Ubuntu使用Qt虚拟键盘,支持中英文切换
前言 最近领导给了个需求,希望将web嵌入到客户端里面,做一个客户端外壳,可以控制程序的启动、停止、重启,并且可以调出键盘在触摸屏上使用(我们的程序虽然是BS架构,但程序还是运行在本地工控机上的),我…...
泰州农商行
该文章用于测试,暴露面检测服务 1595116111115951161112159511611131595116111415951161115159511611161595116111715951161118159511611191595116112015951161121159511611221595116112315951161124159511611251595116112615951161127159511611281595116112915951…...
扫雷(C语言)
目录 前言 一、前提知识 二、扫雷游戏编写 2.2 test文件基本逻辑 2.2.1菜单编写 2.2.2game函数的逻辑 2.2.2.1定义两个数组 2.2.2.2两个数组数组的初始化 2.2.2.3打印棋盘 2.2.2.4布置雷 2.2.2.5排查雷 2.2.2.6获取坐标附近雷的数量 2.2.2.7什么时候…...
【实践功能记录8】使用UseElementSize实现表格高度自适应
一、关于 UseElementSize UseElementSize 是一个 Vue 组合式 API 的实用工具,通常用于获取 DOM 元素的尺寸信息,例如宽度、高度等。它通常与 v-slot 一起使用,以便在模板中直接访问这些尺寸信息。 地址:https://vueuse.org/core/u…...
SMO算法 公式推导
min α 1 2 ∑ i 1 N ∑ j 1 N α i α j y i y j K ( x i ⋅ x j ) − ∑ i 1 N α i s.t. ∑ i 1 N α i y i 0 0 ≤ α i ≤ C , i 1 , 2 , ⋯ , N (9-69) \begin{aligned} & \min_{\alpha} \quad \frac{1}{2} \sum_{i1}^{N} \sum_{j1}^{N} \alpha_i \alpha_j…...
nodejs包管理器pnpm
简介 通常在nodejs项目中我们使用npm或者yarn做为默认的包管理器,但是pnpm的出现让我们的包管理器有了更多的选择,pnpm相比npm具有以下优势: 速度更快,pnpm在安装依赖时,会将依赖包缓存到全局目录,下次安…...
【postman】工具下载安装
postman作用 postman用于测试http协议接口,无论是开发, 还是测试人员, 都有必要学习使用postman来测试接口, 用起来非常方便。 环境安装 postman 可以直接在chrome 上安装插件,当然大部分的同学是没法连接到谷歌商店的,我们可以在电脑本地…...
Java_Springboot核心配置详解
Spring Boot以其简洁、高效和约定优于配置的理念,极大地简化了Java应用的开发流程。在Spring Boot中,核心配置是应用启动和运行的基础。本文将详细介绍Spring Boot中的两种配置文件格式、基础注解的配置方式、自定义配置以及多环境配置。 一、Spring Bo…...
太速科技-9-基于DSP TMS320C6678+FPGA XC7V690T的6U VPX信号处理卡
基于DSP TMS320C6678FPGA XC7V690T的6U VPX信号处理卡 一、概述 本板卡基于标准6U VPX 架构,为通用高性能信号处理平台,系我公司自主研发。板卡采用一片TI DSP TMS320C6678和一片Xilinx公司Virtex 7系列的FPGA XC7V690T-2FFG1761I作为主处理器&#…...
在线UI设计工具:创意与效率的结合
随着UI设计领域的快速增长,设计师们纷纷投身于这一行业,选择一款合适的UI设计工具变得至关重要。除了经典的UI设计软件,在线UI设计工具因其灵活性和便捷性,越来越受到设计师们的喜爱。这种不受时间和地点限制,且不依赖…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序
一、开发环境准备 工具安装: 下载安装DevEco Studio 4.0(支持HarmonyOS 5)配置HarmonyOS SDK 5.0确保Node.js版本≥14 项目初始化: ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序
一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
脑机新手指南(七):OpenBCI_GUI:从环境搭建到数据可视化(上)
一、OpenBCI_GUI 项目概述 (一)项目背景与目标 OpenBCI 是一个开源的脑电信号采集硬件平台,其配套的 OpenBCI_GUI 则是专为该硬件设计的图形化界面工具。对于研究人员、开发者和学生而言,首次接触 OpenBCI 设备时,往…...
华为OD机试-最短木板长度-二分法(A卷,100分)
此题是一个最大化最小值的典型例题, 因为搜索范围是有界的,上界最大木板长度补充的全部木料长度,下界最小木板长度; 即left0,right10^6; 我们可以设置一个候选值x(mid),将木板的长度全部都补充到x,如果成功…...
JS红宝书笔记 - 3.3 变量
要定义变量,可以使用var操作符,后跟变量名 ES实现变量初始化,因此可以同时定义变量并设置它的值 使用var操作符定义的变量会成为包含它的函数的局部变量。 在函数内定义变量时省略var操作符,可以创建一个全局变量 如果需要定义…...
GraphRAG优化新思路-开源的ROGRAG框架
目前的如微软开源的GraphRAG的工作流程都较为复杂,难以孤立地评估各个组件的贡献,传统的检索方法在处理复杂推理任务时可能不够有效,特别是在需要理解实体间关系或多跳知识的情况下。先说结论,看完后感觉这个框架性能上不会比Grap…...
