依赖注入的逻辑基于Java语言

对于一个厨师,要做一道菜。传统的做法是:你需要什么食材,就自己去菜市场买什么。这意味着你必须知道去哪个菜市场、怎么挑选食材、怎么讨价还价等等。你不仅要会做菜,还要会买菜,职责变得复杂了。

而依赖注入就像是有一个贴心的助手,他会提前把你需要的所有食材准备好,直接送到你手上。你只需要专心做菜就行了,不用操心食材从哪里来、怎么来的。

技术层面的深入理解

在软件开发中,**依赖注入(Dependency Injection,简称DI)**是一种设计模式,它的核心思想是:不要让对象自己创建它所依赖的其他对象,而是由外部容器来创建并注入这些依赖对象

传统方式下,如果类A需要使用类B的功能,类A会在内部直接创建类B的实例。这就像厨师自己去买菜一样,造成了紧耦合。依赖注入则是让外部的"容器"来创建类B的实例,然后"注入"给类A使用。

为什么需要依赖注入?

  1. 降低耦合度:类不再负责创建它的依赖对象,只需要声明需要什么依赖
  2. 提高可测试性:可以轻松地注入模拟对象进行单元测试
  3. 增强灵活性:可以在运行时动态地改变依赖关系
  4. 符合开闭原则:对扩展开放,对修改关闭

依赖注入的三种主要方式

  1. 构造器注入:通过构造函数参数注入依赖
  2. Setter注入:通过setter方法注入依赖
  3. 接口注入:通过实现特定接口来注入依赖

Java代码示例演示

第一步:没有依赖注入的传统方式

// 数据库服务类
class DatabaseService {public void save(String data) {System.out.println("保存数据到数据库: " + data);}
}// 邮件服务类
class EmailService {public void sendEmail(String message) {System.out.println("发送邮件: " + message);}
}// 用户服务类 - 传统方式(紧耦合)
class UserService {private DatabaseService databaseService;private EmailService emailService;public UserService() {// 直接在内部创建依赖对象 - 这就是紧耦合的问题所在this.databaseService = new DatabaseService();this.emailService = new EmailService();}public void registerUser(String username) {// 保存用户信息databaseService.save("用户: " + username);// 发送欢迎邮件emailService.sendEmail("欢迎 " + username + " 注册我们的系统!");}
}

问题分析:

  • UserService直接创建了DatabaseService和EmailService的实例
  • 如果要换成其他类型的数据库或邮件服务,必须修改UserService的代码
  • 难以进行单元测试,因为无法模拟这些依赖对象

第二步:使用依赖注入重构

首先定义接口,实现依赖倒置:

// 定义数据库服务接口
interface DatabaseServiceInterface {void save(String data);
}// 定义邮件服务接口
interface EmailServiceInterface {void sendEmail(String message);
}// MySQL数据库实现
class MySQLDatabaseService implements DatabaseServiceInterface {@Overridepublic void save(String data) {System.out.println("保存数据到MySQL数据库: " + data);}
}// MongoDB数据库实现
class MongoDatabaseService implements DatabaseServiceInterface {@Overridepublic void save(String data) {System.out.println("保存数据到MongoDB: " + data);}
}// SMTP邮件服务实现
class SMTPEmailService implements EmailServiceInterface {@Overridepublic void sendEmail(String message) {System.out.println("通过SMTP发送邮件: " + message);}
}// 短信服务实现(也可以发送通知)
class SMSService implements EmailServiceInterface {@Overridepublic void sendEmail(String message) {System.out.println("发送短信通知: " + message);}
}

第三步:构造器注入方式

class UserService {private final DatabaseServiceInterface databaseService;private final EmailServiceInterface emailService;// 通过构造器注入依赖public UserService(DatabaseServiceInterface databaseService, EmailServiceInterface emailService) {this.databaseService = databaseService;this.emailService = emailService;}public void registerUser(String username) {databaseService.save("用户: " + username);emailService.sendEmail("欢迎 " + username + " 注册我们的系统!");}
}

第四步:Setter注入方式

class UserServiceWithSetter {private DatabaseServiceInterface databaseService;private EmailServiceInterface emailService;// 通过setter方法注入依赖public void setDatabaseService(DatabaseServiceInterface databaseService) {this.databaseService = databaseService;}public void setEmailService(EmailServiceInterface emailService) {this.emailService = emailService;}public void registerUser(String username) {if (databaseService == null || emailService == null) {throw new IllegalStateException("依赖服务未正确注入!");}databaseService.save("用户: " + username);emailService.sendEmail("欢迎 " + username + " 注册我们的系统!");}
}

第五步:简单的依赖注入容器

import java.util.HashMap;
import java.util.Map;// 简单的依赖注入容器
class DIContainer {private Map<Class<?>, Object> services = new HashMap<>();// 注册服务public <T> void register(Class<T> serviceType, T implementation) {services.put(serviceType, implementation);}// 获取服务@SuppressWarnings("unchecked")public <T> T getService(Class<T> serviceType) {return (T) services.get(serviceType);}// 创建UserService实例,自动注入依赖public UserService createUserService() {DatabaseServiceInterface dbService = getService(DatabaseServiceInterface.class);EmailServiceInterface emailService = getService(EmailServiceInterface.class);if (dbService == null || emailService == null) {throw new IllegalStateException("必要的服务未注册到容器中!");}return new UserService(dbService, emailService);}
}

第六步:完整的使用示例

public class DependencyInjectionDemo {public static void main(String[] args) {// 创建依赖注入容器DIContainer container = new DIContainer();// 注册具体的实现到容器中container.register(DatabaseServiceInterface.class, new MySQLDatabaseService());container.register(EmailServiceInterface.class, new SMTPEmailService());// 通过容器创建UserService,依赖会自动注入UserService userService = container.createUserService();// 使用服务userService.registerUser("张三");System.out.println("\n--- 切换到不同的实现 ---");// 可以轻松切换到不同的实现container.register(DatabaseServiceInterface.class, new MongoDatabaseService());container.register(EmailServiceInterface.class, new SMSService());UserService userService2 = container.createUserService();userService2.registerUser("李四");System.out.println("\n--- 演示Setter注入 ---");// 演示Setter注入UserServiceWithSetter userServiceSetter = new UserServiceWithSetter();userServiceSetter.setDatabaseService(new MySQLDatabaseService());userServiceSetter.setEmailService(new SMTPEmailService());userServiceSetter.registerUser("王五");}
}

第七步:单元测试的便利性

// 模拟对象用于测试
class MockDatabaseService implements DatabaseServiceInterface {public boolean saveCalled = false;public String savedData = null;@Overridepublic void save(String data) {saveCalled = true;savedData = data;System.out.println("模拟保存: " + data);}
}class MockEmailService implements EmailServiceInterface {public boolean emailSent = false;public String sentMessage = null;@Overridepublic void sendEmail(String message) {emailSent = true;sentMessage = message;System.out.println("模拟发送邮件: " + message);}
}// 简单的测试示例
class UserServiceTest {public static void testUserRegistration() {// 创建模拟对象MockDatabaseService mockDB = new MockDatabaseService();MockEmailService mockEmail = new MockEmailService();// 注入模拟对象UserService userService = new UserService(mockDB, mockEmail);// 执行测试userService.registerUser("测试用户");// 验证结果System.out.println("数据库保存被调用: " + mockDB.saveCalled);System.out.println("保存的数据: " + mockDB.savedData);System.out.println("邮件发送被调用: " + mockEmail.emailSent);System.out.println("发送的邮件: " + mockEmail.sentMessage);}public static void main(String[] args) {System.out.println("=== 单元测试演示 ===");testUserRegistration();}
}

总结

依赖注入就像是一个智能的"服务管家",它负责管理和协调各个对象之间的依赖关系。通过这种方式:

  1. 代码更加灵活:可以轻松地切换不同的实现
  2. 测试更加容易:可以注入模拟对象进行隔离测试
  3. 维护更加简单:修改依赖关系不需要改动核心业务逻辑
  4. 扩展更加方便:添加新功能只需要实现接口并注册到容器

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

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

相关文章

skywalking镜像应用springboot的例子

目录 1、skywalking-ui连接skywalking-oap服务失败问题 2、k8s环境 检查skywalking-oap服务状态 3、本地iidea启动服务连接skywalking oap服务 4、基于apache-skywalking-java-agent-9.4.0.tgz构建skywalking-agent镜像 4.1、Dockerfile内容如下 4.2、AbstractBuilder.M…

3. java 堆和 JVM 内存结构

1. JVM介绍和运行流程-CSDN博客 2. 什么是程序计数器-CSDN博客 3. java 堆和 JVM 内存结构-CSDN博客 4. 虚拟机栈-CSDN博客 5. JVM 的方法区-CSDN博客 6. JVM直接内存-CSDN博客 7. JVM类加载器与双亲委派模型-CSDN博客 8. JVM类装载的执行过程-CSDN博客 9. JVM垃圾回收…

UnityShader——SSAO

目录 1.是什么 2.原理 3.各部分解释 2.1.从屏幕空间到视图空间 2.2.以法线半球为基&#xff0c;获取随机向量 2.3.应用偏移&#xff0c;并将其转换为uv坐标 2.4.获取深度 2.5.比较并计算贡献 2.6.最后计算 4.改进 4.1.平滑过渡 4.2.模糊 5.变量和语句解释 5.1._D…

【设计模式】外观模式(门面模式)

外观模式&#xff08;Facade Pattern&#xff09;详解一、外观模式简介 外观模式&#xff08;Facade Pattern&#xff09; 是一种 结构型设计模式&#xff0c;它为一个复杂的子系统提供一个统一的高层接口&#xff0c;使得子系统更容易使用。 外观模式又称为门面模式&#xff0…

【6.1.1 漫画分库分表】

漫画分库分表 “数据量大了不可怕&#xff0c;可怕的是不知道如何优雅地拆分。” &#x1f3ad; 人物介绍 架构师老王&#xff1a;资深数据库架构专家&#xff0c;精通各种分库分表方案Java小明&#xff1a;对分库分表充满疑问的开发者ShardingSphere师傅&#xff1a;Apache S…

Tomcat问题:启动脚本startup.bat中文乱码问题解决

一、问题描述 我们第一次下载或者打开Tomcat时可能在控制台会出现中文乱码问题二、解决办法 我的是8.x版本的tomcat用notepad打开&#xff1a;logging.properties 找到&#xff1a;java.util.logging.ConsoleHandler.encoding设置成GBK&#xff0c;重启tomcat即可

Linux中Gitee的使用

一、Gitee简介&#xff1a;Gitee&#xff08;码云&#xff09;是中国的一个代码托管和协作开发平台&#xff0c;类似于GitHub或GitLab&#xff0c;主要面向开发者提供代码管理、项目协作及开源生态服务。适用场景个人开发者&#xff1a;托管私有代码或参与开源项目。中小企业&a…

Oracle大表数据清理优化与注意事项详解

一、性能优化策略 1. 批量处理优化批量大小选择&#xff1a; 小批量(1,000-10,000行)&#xff1a;减少UNDO生成&#xff0c;但需要更多提交次数中批量(10,000-100,000行)&#xff1a;平衡性能与资源消耗大批量(100,000行)&#xff1a;适合高配置环境&#xff0c;但需监控资源使…

Anaconda及Conda介绍及使用

文章目录Anaconda简介为什么选择 Anaconda&#xff1f;Anaconda 安装Win 平台macOS 平台Linux 平台Anaconda 界面使用Conda简介Conda下载安装conda 命令环境管理包管理其他常用命令Jupyter Notebook&#xff08;可选&#xff09;Anaconda简介 Anaconda 是一个数据科学和机器学…

外包干了一周,技术明显退步

我是一名本科生&#xff0c;自2019年起&#xff0c;我便在南京某软件公司担任功能测试的工作。这份工作虽然稳定&#xff0c;但日复一日的重复性工作让我逐渐陷入了舒适区&#xff0c;失去了前进的动力。两年的时光匆匆流逝&#xff0c;我却在原地踏步&#xff0c;技术没有丝毫…

【QT】多线程相关教程

一、核心概念与 Qt 线程模型 1.线程与进程的区别: 线程是程序执行的最小单元&#xff0c;进程是资源分配的最小单元&#xff0c;线程共享进程的内存空间(堆&#xff0c;全局变量等)&#xff0c;而进程拥有独立的内存空间。Qt线程只要关注同一进程内的并发。 2.为什么使用多线程…

VS 版本更新git安全保护问题的解决

问题&#xff1a;我可能移动了一个VS C# 项目&#xff0c;然后&#xff0c;发现里面的git版本检测不能用了 正在打开存储库: X:\Prj_C#\3D fatal: detected dubious ownership in repository at X:/Prj_C#/3DSnapCatch X:/Prj_C#/3D is owned by:S-1-5-32-544 but the current …

Git常用命令一览

Git 是基于 Linux内核开发的版本控制工具。与常用的版本控制工具 CVS, Subversion 等不同&#xff0c;它采用了分布式版本库的方式&#xff0c;不必服务器端软件支持&#xff08;ps&#xff1a;这得分是用什么样的服务端&#xff0c;使用http协议或者git协议等不太一样。并且在…

基于 JSON 文件定位图片缺陷点并保存

基于JSON的图片缺陷处理流程 ├── 1. 输入检查 │ ├── 验证图片文件是否存在 │ └── 验证JSON文件是否存在 │ ├── 2. 数据加载 │ ├── 打开并加载图片 │ └── 读取并解析JSON文件 │ ├── 3. 缺陷信息提取 │ ├── 检查JSON中是否存在shapes字…

Redis基础学习(五大值数据类型的常用操作命令)

目录 一、Redis基本知识与Redis键&#xff08;key&#xff09;常用操作命令。 二、Redis的五大值的数据类型。&#xff08;value&#xff09; 三、Redis关于键&#xff08;key&#xff09;的值常用操作指令表格统计。 &#xff08;1&#xff09;字符串&#xff08;String&#…

Ubuntu——办公软件 LibreOffice 安装与使用指南

十四、LibreOffice 安装与使用1、核心组件组件​​​​图标​​​​对应MS Office​​​​核心功能定位​​​​Writer​​&#x1f4dd;Word专业文档处理与排版​​Calc​​&#x1f4ca;Excel数据计算与分析​​Impress​​&#x1f3ac;PowerPoint演示文稿制作​​Draw​​&…

Securecrt丢失tab以及终端重新配色

今天在使用 Securecrt 的时候&#xff0c;发现 Securecrt 的 tab 标签消失不见了&#xff0c;仔细回想起来&#xff0c;应该是上一次误按了 alt enter 最大化&#xff0c;然后导致配置丢失的问题 还有表现就是菜单中的 Session Tabs 无论勾选还是不勾选都没有任何变化&#xf…

frp搭建内网穿透教程

frp搭建内网穿透教程 步骤1&#xff1a;准备工作 公网服务器&#xff1a;需要一台具有公网IP的服务器作为中转服务器&#xff0c;安装frp服务器端&#xff08;frps&#xff09;。内网设备&#xff1a;需要暴露服务的内网设备&#xff0c;安装frp客户端&#xff08;frpc&#xf…

【JavaEE进阶】图书管理系统(未完待续)

目录 用户登录 添加图书 图书列表 修改图书 删除图书 批量删除 拦截器 &#x1f343;前言 什么是拦截器? 拦截器的基本使用 自定义拦截器 注册配置拦截器 拦截路径 拦截器执行流程 项目实现统一拦截 定义拦截器 注册配置拦截器 前⾯图书管理系统, 咱们只完成了⽤⼾登录和图书列…

基于同花顺API的熊市与牛市识别模型开发及因子分析

基于同花顺API的熊市与牛市识别模型开发及因子分析 1. 引言 1.1 研究背景与意义 金融市场中的牛市与熊市识别一直是投资者和研究人员关注的重点问题。牛市(Bull Market)通常指价格持续上涨的市场环境,投资者信心充足,交易活跃;而熊市(Bear Market)则指价格持续下跌的市场…