设计模式:依赖倒转原则 - 依赖抽象,解耦具体实现

news2025/4/15 10:37:16

一、为什么用依赖倒转原则?

在软件开发中,类与类之间的依赖关系是架构设计中的关键。如果依赖过于紧密,系统的扩展性和维护性将受到限制。为了应对这一挑战,依赖倒转原则(Dependency Inversion Principle,DIP)应运而生。它旨在通过解耦高层模块与低层模块,从而提升系统的灵活性和可维护性。

依赖倒转原则的核心思想:

• 高层模块不应依赖低层模块,二者都应依赖于抽象。

• 抽象不应依赖细节,细节应依赖抽象。

通过遵循这一原则,能够降低模块之间的耦合度。当需求变更时,影响的范围更小,修改更加安全。

二、依赖倒转原则实例

示例代码如下:通过对比来看,依赖倒转原则的优势。

场景:一个用户管理系统,系统需要进行邮件通知。

1.反例:不遵守依赖倒转原则

低层模块 EmailService

// 低层模块:邮件发送功能
public class EmailService {
    public void sendEmail(String message) {
        System.out.println("Sending email: " + message);
    }
}

高层模块 UserManager

// 高层模块:用户管理
public class UserManager {
    private EmailService emailService;

    public UserManager() {
    // 高层模块直接依赖低层模块
        this.emailService = new EmailService(); 
    }

    public void registerUser(String username) {
        System.out.println("Registering user: " + username);
        emailService.sendEmail("Welcome to the system, " + username);
    }
}

缺点:

UserManager类直接依赖了EmailService类,EmailService的变化会影响到UserManager,UserManager的功能无法灵活变化。

2.正例:遵守依赖倒转原则

第1步:抽象接口 NotificationService

// 抽象接口:通知服务
public interface NotificationService {
    void sendNotification(String message);
}

第2步:低层模块 EmailService

// 低层模块:邮件发送功能(具体实现)
public class EmailService implements NotificationService {

    @Override
    public void sendNotification(String message) {
        System.out.println("Sending email: " + message);
    }
}

第3步:高层模块 UserManager

// 高层模块:用户管理
public class UserManager {

    private NotificationService notificationService;

    // 构造函数,接收 NotificationService 类型的抽象
    public UserManager(NotificationService notificationService) {
        this.notificationService = notificationService;
    }

    // 用户注册功能
    public void registerUser(String username) {
        System.out.println("Registering user: " + username);
        notificationService.sendNotification("Welcome to the system, " + username);
    }
}

第4步:测试类 Main

// 测试用的 Main 方法
public class Main {
    public static void main(String[] args) {
        // 创建具体的底层模块:邮件发送服务
        NotificationService emailService = new EmailService();

        // 创建高层模块:用户管理,并传入具体的底层模块
        UserManager userManager = new UserManager(emailService);

        // 调用用户注册功能
        userManager.registerUser("JohnDoe");
    }
}

代码说明:

• NotificationService 接口:发送通知的抽象方法,任何具体的通知服务都实现此接口。

• EmailService:底层模块,是通知服务接口的具体类。它用于处理邮件发送逻辑。

• UserManager:高层模块,它通过构造函数接受NotificationService接口的依赖,从而不直接依赖于EmailService类。

• Main 类:程序入口,创建了邮件服务类,并将其注入到用户管理类中,最后调用registerUser方法进行注册并发送邮件通知。

运行结果:

Registering user: JohnDoe
Sending email: Welcome to the system, JohnDoe

优化后的优势:

• 依赖于抽象,而非具体实现:

UserManager类依赖于NotificationService接口,而不是具体的EmailService类。

这样,如果更改通知方式(如短信通知),只需实现一个新的NotificationService接口实现类(如SmsService),并将其注入到UserManager中,而无需修改UserManager类代码。

• 提升灵活性与扩展性:

通过依赖注入,UserManager 类与 Email-Service 解耦,使得系统更易于测试和扩展,满足不同需求的变化。

三、依赖倒转原则的价值

• 降低耦合度:

高层模块不再依赖低层模块的实现细节,而是依赖于抽象,从而大大降低了模块间的耦合。

• 提高灵活性与扩展性:

系统能够轻松适应需求变化和技术变更。如新增功能或模块时(如替换邮件通知为推送通知),只需产出新的实现类, 而无需修改高层模块的代码。

• 简化测试:

通过抽象化,解耦了模块间的依赖,单元测试更容易。此外,可通过模拟(mock)替代依赖的实现,进行独立测试。

四、适用场景

以下场景都能充分发挥依赖倒转原则的价值:

• 需求变化频繁的系统

• 功能模块扩展

• 高可测试性的系统

五、总结

依赖倒转原则强调高层模块与低层模块都依赖于抽象接口,从而减少耦合、提高系统的灵活性和可扩展性。

在实际开发中,遵循这一原则能够设计出更具适应性的系统,使得需求变化和功能扩展变得更加简单和安全。

同时,它还能提升系统的可维护性和可测试性,是构建高质量软件系统的关键,尤其适用于复杂或需求变动频繁的项目。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2334125.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

探秘Transformer系列之(26)--- KV Cache优化 之 PD分离or合并

探秘Transformer系列之(26)— KV Cache优化 之 PD分离or合并 文章目录 探秘Transformer系列之(26)--- KV Cache优化 之 PD分离or合并0x00 概述0x01 背景知识1.1 自回归&迭代1.2 KV Cache 0x02 静态批处理2.1 调度策略2.2 问题…

C++语言程序设计——02 变量与数据类型

目录 一、变量与数据类型(一)变量的数据类型(二)变量命名规则(三)定义变量(四)变量赋值(五)查看数据类型 二、ASCII码三、进制表示与转换(一&…

Model Context Protocol (MCP) - 尝试创建和测试一下MCP Server

1.简单介绍 MCP是Model Context Protocol的缩写,是Anthropic开源的一个标准协议。MCP使得大语言模型可以和外部的数据源,工具进行集成。当前MCP在社区逐渐地流行起来了。同时official C# SDK(仓库是csharp-sdk) 也在不断更新中,目前最新版本…

python文件打包无法导入ultralytics模块

💥打包的 .exe 闪退了?别慌!教你逐步排查 PyInstaller 打包的所有错误! 🛠 运行 .exe 查看报错信息✅ 正确姿势: ⚠ importlib 动态导入导致打包失败❓什么是动态导入?✅ 解决方式: …

AMBA-CHI协议详解(二十六)

AMBA-CHI协议详解(一)- Introduction AMBA-CHI协议详解(二)- Channel fields / Read transactions AMBA-CHI协议详解(三)- Write transactions AMBA-CHI协议详解(四)- Other transactions AMBA-CHI协议详解(五)- Transaction identifier fields AMBA-CHI协议详解(六…

Go小技巧易错点100例(二十六)

本期分享: 1. string转[]byte是否会发生内存拷贝 2. Go程序获取文件的哈希值 正文: string转[]byte是否会发生内存拷贝 在Go语言中,字符串转换为字节数组([]byte)确实会发生内存拷贝。这是因为在Go中,字…

FPGA_BD Block Design学习(一)

PS端开发流程详细步骤 1.第一步:打开Vivado软件,创建或打开一个工程。 2.第二步:在Block Design中添加arm核心,并将其配置为IP核。 3.第三步:配置arm核心的外设信息,如DDR接口、时钟频率、UART接口等。 …

ubuntu20.04+qt5.12.8安装serialbus

先从官网https://download.qt.io/archive/qt/5.12/5.12.8/submodules/ 下载 qtserialbus-everywhere-src-5.12.8.tar.xz 有需要其他版本的点击返回上一级自行寻找对应版本。 也可从 https://download.csdn.net/download/zhouhui1982/90595810 下载 在终端中依次输入以下命令…

如何查看自己抖音的IP属地?详细教程+常见问题解答

在当今互联网时代,IP属地信息已成为各大社交平台(如抖音、微博、快手等)展示用户真实网络位置的重要功能。无论是出于隐私保护、账号安全,还是单纯好奇自己的IP归属地,了解如何查看抖音IP属地都很有必要。 本文将详细介…

⑪数据中心网络M-LAG实战

一、DeviceA-M-LAG-Mater配置 1、M-LAG 系统配置。 # m-lag mad exclude interface GigabitEthernet1/0/7 m-lag mad exclude interface Vlan-interface100 m-lag mad exclude interface Vlan-interface101 m-lag system-mac 0002-0002-0002 m-lag system-number 1 m-la…

化工企业数字化转型:从数据贯通到生态重构的实践路径

一、战略定位:破解行业核心痛点 化工行业面临生产安全风险高(全国危化品企业事故率年增5%)、能耗与排放压力大(占工业总能耗12%)、供应链协同低效(库存周转率低于制造业均值30%)三大挑战。《石…

JAVA——初识JAVA

文章目录 如何在cmd上编译、运行代码解析String[] args中放的是什么Java结构编译运行可能遇到的错误Java中的注释Java的三种注释编码不一致的问题 IDEA常用基础开发快捷键补齐快捷键注释快捷键 IDEA的基础调式方法标识符 如何在cmd上编译、运行 在没有集成开发环境下&#xff…

Shell脚本的学习

编写脚本文件 定义以开头:#!/bin/bash #!用来声明脚本由什么shell解释,否则使用默认shel 第一步:编写脚本文件 #!/bin/bash #注释 echo "这是输出" 第二步:加上执行权限:chmod x 脚本文件名.sh 第三步&…

专题十四:动态路由——OSPF

一、OSPF简介 开放式最短路径优先OSPF(Open Shortest Path First)是IETF组织开发的一个基于链路状态的内部网关协议(Interior Gateway Protocol),采用DIjkstra算法,协议号是89。用于自治系统(A…

Cocos Creator Shader入门实战(八):Shader实现圆形、椭圆、菱形等头像

引擎:3.8.5 您好,我是鹤九日! 回顾 Shader的学习是一条漫长的道路。 理论知识的枯燥无味,让很多人选择了放弃。然而不得不说:任何新知识、新领域的学习,本身面临的都是问题! 互联网和AI给了我…

【AI论文】VCR-Bench:视频链式思考推理的综合评估框架

摘要:思想链(CoT)推理的进步显著增强了大型语言模型(LLMs)和大型视觉语言模型(LVLMs)的能力。 然而,目前仍然缺乏一个严格的视频CoT推理评估框架。 目前的视频基准测试无法充分评估推…

数据中台、BI业务访谈(二):组织架构梳理的坑

这是数据中台、BI业务访谈系列的第二篇文章,在上一篇文章中,我重点介绍了在给企业的业务部门、高层管理做业务访谈之前我们要做好行业、业务知识的功课。做好这些功课之后,就到了实际的访谈环节了。 业务访谈关键点 那么在具体业务访谈的时…

【零基础实战】Ubuntu搭建DVWA漏洞靶场全流程详解(附渗透测试示例)

【零基础实战】Ubuntu搭建DVWA漏洞靶场全流程详解(附渗透测试示例) (声明:实际操作请遵守网络安全法,仅在授权环境进行测试,仅供个人研究) 一、DVWA靶场简介 DVWA(Damn Vulnerable Web Application)是专为网络安全学习者设计的漏洞演练平台,包含SQL注入、XSS、文件…

库学习04——numpy

一、基本属性 二、 创建数组 (一)arange a np.arange(10,20,2) # [10,12,14,16,18] 只有一个参数n的话,默认是从0到n-1的一维数组。 (二)自定义reshape a np.arange(12).reshape((3,4)) [[ 0 1 2 3][ 4 5 …

Win10系统安装WSL2-Ubuntu, 并使用VScode开始工作

本教程基于博主当前需要使用 WSL2(Windows Subsystem for Linux 2) 而编写,将自己使用的经过分享给大家。有什么意见建议敬请大家批评指正。此过程需要打开 Microsoft Store 话不多说,立即开始~ 文章目录 1. 检查系统版本2. 启动 WSL 功能3. 安装Ubuntu4…