依赖注入的优缺点与最佳实践

news2024/11/16 23:59:08

1 什么是依赖注入

依赖注入,全称Dependency Injection,简称DI。

在我们深入探讨之前,先来聊聊“依赖”和“注入”这两个比较术语的词。打个比方,你可以把“依赖”想象成电器设备的外接电线,而“注入”就像是把这根电线插到电源插座里,使用依赖注入就好比是你不需要知道电源插座在哪里,只需要知道有人会在你需要的时候为你插上插座,让电流连通,使设备工作。

在软件设计中,“依赖”指的是一个类需要调用的其他类或者服务,而“注入”则是指将这些所需的类或服务传递给需要它们的类的过程。使用“依赖注入”技术,我们可以在运行时将依赖关系传递给对象,而不是让对象自己去创建或查找它们需要的依赖。

如此,则软件中的组件可以在不同环境下重复使用,就像家里的电器可以随意在不同的插座上使用一样,不用担心它们的电源问题。“依赖注入”让我们的代码更加灵活,更容易维护和扩展。

2 为什么需要依赖注入

2.1 为什么需要控制反转

依赖注入其实是控制反转(IOC)的一种实现方式。

控制反转(Inversion of Control,IOC)是一种设计原则,它让我们能够将程序的控制权从程序本身转移到外部容器或框架上。需要控制反转的具体理由主要包括如下几点:

  • 实际只需要对象提供的服务,不需要关心对象从何而来。
  • 时空转变时,可能需要不同的对象来提供类似的服务,比如对于数据库操作服务,单元测试时需要模拟操作,而实际运行时需要真实操作。
  • 拥有对象的控制权时,需要不断的改造自己,麻烦且容易出问题。比如依赖项的构造函数变化、具体实现的更改等。
  • 将对象创建的控制权交出去,让外部场景来提供合适的服务对象。这可以让程序更容易组件化,更方便组合。

想象一下,如果你是一位导演,你肯定希望能够专注于电影拍摄部分,而不是每天都忙于处理拍摄场地的预订、设备的采购这些琐事。这就是控制反转的魅力所在——它允许我们专注于核心业务逻辑,而将创建对象、管理生命周期等琐事交给框架去处理。

2.2 控制反转的需求举例:发消息

我们的业务需要向用户发送消息。最开始,我们可能使用短信来实现这一功能。随着技术的发展,我们可能改用微信消息,未来甚至可能使用一种全新的通讯方式——比如X信。

如果我们的代码直接依赖于某种特定的消息发送方式,每当需要改变时,我们都需要修改代码,这无疑是非常繁琐和容易出错的。控制反转让我们可以轻松应对这种变化,因为我们只需更换提供服务的对象即可。

2.3 控制反转的其他实现:依赖查找(DL)

除了依赖注入,依赖查找(Dependency Lookup,DL)也是实现控制反转的一种方式。它的思路是在需要的时候,我们主动向一个管理容器询问或查找我们需要的依赖。这种方式比较传统,仍然依赖于容器的API,就像你需要知道每个插座在哪里,以及如何打开开关一样,这对于软件的解耦并不是最佳选择。

3 实现方式

3.1 使用接口

通过接口来抽象依赖是一个比较好的编程实践。在这种情况下,组件不会直接创建它们需要的依赖,而是通过引用接口来获得这些依赖的具体实现。这就像是制定一个规则,所有需要电力的设备都必须有一个标准的插头,这样它们就可以从任何一个标准插座获得电力。

注意使用接口不是必需的,你完全可以将一个不实现接口的类型实例注入到程序中。

3.2 基于set方法

在这种情况下,组件会提供一个公开的set方法,容器可以通过这个方法将依赖传递给组件。这就好比每个电器都有一个开关,当需要电力时,你只需打开开关即可。

3.3 基于构造函数

基于构造函数的注入是最直接的方式之一。当创建一个新的对象时,我们可以在构造函数中传递所有需要的依赖。这就像是买一个需要电池的设备时,店家直接给你装好电池,你拿回家就可以直接使用。

3.4 基于注解

在一些现代编程语言中,我们还可以使用注解(Annotations)来实现依赖注入。例如,在Java中,我们可以在构造函数、set方法、私有字段等元素上添加"@Autowired"注解,这样容器就会自动为我们注入依赖。

这就像是有一个自动插电系统,你只需要打开电器,它就会自动为你供电。

注意Spring中不再推荐使用"@Autowired"直接注解字段,因为这样写容易出Bug,还会让程序和依赖注入框架产生耦合。

4 开发语言支持

很多开发语言本身或者通过第三方框架提供了对依赖注入的支持,下边是一些介绍。

4.1 C++

在C++标准库中没有对依赖注入的支持,但是社区有Google Fruit、Boost.DI、Hypodermic这样的框架,它们提供了依赖注入的能力。使用这些框架,C++开发者可以更容易地管理对象的生命周期和依赖关系。

4.2 Java

Java可能是依赖注入最为流行的领域之一,Spring Framework就是一个典型的例子。它提供了一整套依赖注入的解决方案,极大地简化了Java应用的开发。

4.3 .NET

在.NET世界中,ASP.NET Core默认就支持依赖注入,另外还可以选择Ninject、Autofac、Spring.NET等框架。这些框架使得.NET开发者可以轻松地实现依赖注入,使他们的应用程序更加模块化和可测试。

4.4 PHP

PHP也不例外,Phalcon和Laravel等框架提供了依赖注入的功能,使得PHP开发更加现代化,易于管理和维护。

---

这里为了让大家更好的感受,我们举一个Spring Boot的例子:

首先声明一个日志接口:

public interface ILogger {
    void log(String message);
}

然后创建一个实现类,我们使用 @Component 注解来标记 ConsoleLogger 作为一个可注入的组件。

import org.springframework.stereotype.Component;

@Component
public class ConsoleLogger implements ILogger {
    @Override
    public void log(String message) {
        System.out.println("Log: " + message);
    }
}

然后创建一个 Application 类,它使用 @Service 注解,并通过构造函数注入 ILogger 依赖。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class Application {
    private final ILogger logger;

    @Autowired
    public Application(ILogger logger) {
        this.logger = logger;
    }

    public void run() {
        logger.log("Application is running.");
    }
}

在 SpringApplicationMain 主类中注入 Application 类的实例并运行它。SpringApplicationMain 类实现了 CommandLineRunner 接口,当 Spring Boot 应用启动时,run 方法将被自动调用,然后会执行 application.run()。

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class SpringApplicationMain implements CommandLineRunner {

    private final Application application;

    public SpringApplicationMain(Application application) {
        this.application = application;
    }

    public static void main(String[] args) {
        SpringApplication.run(SpringApplicationMain.class, args);
    }

    @Override
    public void run(String... args) {
        application.run();
    }
}

优缺点

5.1 优点

  • 代码解耦:通过依赖注入,我们可以降低类与类之间的耦合度。这就好比你的手机、笔记本电脑和台式机可以使用同一个充电器一样方便。这种解耦让每个类更加专注于自己的职责,而不是担心如何与其他类协作。
  • 测试便利性:当你需要对某个类进行单元测试时,你可以轻松地替换它依赖的组件,使用模拟(Mock)对象来代替真实的实现。
  • 可维护性和可扩展性:随着业务的发展,需求会发生变化。依赖注入使得我们可以不改变现有代码的情况下,扩展或替换组件。这就像是为了应对不同的电压,你可以更换不同的电源适配器,而不是去修改电器本身。

5.2 缺点

  • 学习曲线:对于初学者来说,依赖注入可能会带来一定的学习挑战。理解控制反转和依赖注入的概念,以及如何在项目中正确使用它们,可能需要一定的时间和实践。
  • 过度设计:在一些简单的应用场景中,使用依赖注入可能会使得项目过度设计,增加不必要的复杂性。这就像是用一个巨大的电源站来给一盏台灯供电,虽然可行,但显然不是最优解。
  • 运行时性能:虽然现代依赖注入框架的性能已经非常优秀,但在一些性能敏感的场景下,依赖注入可能会引入轻微的性能开销。这种开销可以比喻为插座和电器之间连接的电线长度,虽然影响微乎其微,但在某些情况下需要考虑。

6 最佳实践

明确你的依赖:在使用依赖注入时,首先要明确你的类需要哪些外部依赖。这就像是在你搭建电路之前,需要知道哪些设备需要电源。

选择合适的注入方式:根据你的具体情况选择最合适的依赖注入方式,无论是setter注入还是构造器注入。这就像是选择合适的充电器插头,确保它与你的设备兼容。

避免过多的依赖:一个类不应该有太多的依赖,这会使得类变得难以管理和维护。想象一下,如果一个电器有太多的插头,使用起来就会变得非常麻烦。

使用依赖注入容器:使用依赖注入容器可以帮助你管理类的依赖关系,这就像是使用一个多功能的电源条,可以同时为多个设备供电。

结语

依赖注入作为一种设计模式,它旨在减少代码之间的耦合性,使代码更易于维护和测试。依赖注入的核心思想是将对象的创建与它们的使用分离开来。通过这种方式,依赖注入可以使代码更加灵活,更容易扩展和重用。随着软件行业的不断发展,依赖注入也在不断进化,它已经从一个编程技巧变成了一种软件设计的基本原则。

关注萤火架构,加速技术提升!

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

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

相关文章

热过载继电器 WJJL1-05/2X AC220V 0.5A-5A 导轨安装 JOSEF约瑟

系列型号 WJJL1-10D/1过载保护器;WJJL1-50D/1过载保护器; WJJL1-100D/1过载保护器;WJJL1-300D/1过载保护器; WJJL1-600D/1过载保护器;WJJL1-1000D/1过载保护器; WJJL1-2000D/1过载保护器;WJ…

React Native 桥接组件封装原生组件属性

自定义属性可以让组件具备更多的灵活性,所以有必要在JS 层通过自定义属性动态传值。 一、添加原生组件属性 因为 ViewManager 管理了整个组件的行为,所以要新增组件属性也需要在这里面(如 InfoViewManager)进行定义。 1、在Inf…

使用Web自动化测试工具显著好处

随着互联网技术的飞速发展,Web应用程序在企业中的重要性不断上升。为了确保Web应用程序的质量和稳定性,许多企业转向了Web自动化测试工具。下面是使用Web自动化测试工具的一些显著好处: 1. 提高测试覆盖率 Web自动化测试工具可以模拟用户与We…

软件测试|解读Python的requirements.txt文件:管理项目依赖的完整指南

简介 在Python项目中,管理依赖库是必不可少的。requirements.txt文件是一种常用的方式,用于列出项目所需的所有依赖库及其版本。本文将详细介绍requirements.txt的用法,帮助你更好地管理项目的依赖。 使用步骤 创建requirements.txt文件&am…

二叉树-遍历-单独精讲

文章目录 遍历中序遍历/节点的中序前序遍历-节点的前序后序遍历-节点的后序三序综合13-Apush前/前序前13-Bpush前/中序前13-Cpush前/后序前 两序重叠示例一13前序前13中序前 示例二13前序前13后序前 示例三13中序前13后序前 遍历 遍历 即:遍历每个元素。 for遍历只会遍历每个…

Kafka配置Kerberos安全认证及与Java程序集成

Background 本文主要介绍在 Kafka 中如何配置 Kerberos 认证,以及 java 使用 JAAS 来进行 Kerberos 认证连接。本文演示为单机版。 所用软件版本 查看 Kerberos 版本命令:klist -V 软件名称版本jdk1.8.0_202kafka2.12-2.2.1kerberos1.15.1 1、Kerberos …

RT-Thread:STM32 PHY 调试,使用软件包 WIZNET 驱动 W5500

说明: 1. 本文记录使用 RT-Thread 软件包 WIZNET驱动 W5500 的调试笔记。 2. 采用 RT-Thread Studio 工程 STM32F407VET6 芯片,W5500 PHY芯片,两者之间使用SPI接口链接 。 注意: 1.在按流程建立工程,和移植完 wizn…

ASP.NET摄影展示网站源码

ASP.NET摄影展示网站源码 项目描述 网站利用了ext技术,用户自定义了展示控件 前台展示类别有: 协会动态,摄影理论,影展影赛,采风路线, 影友之窗,佳作欣赏,器材专区,展览信…

「达摩院MindOpt」优化FlowShop流水线作业排班问题

FlowShop流水线作业 在企业在面临大量多样化的生产任务时,如何合理地安排流水线作业以提高生产效率及确保交货期成为了一个重要的问题。 一个典型的问题就是FlowShop流水线作业安排问题,也有称为生产下料问题。它涉及到多台机器、多个工序以及多个作业的调度安排。…

QWebEngineView类方法、属性、信号与槽汇总

文章目录 📖 介绍 📖🏡 环境 🏡📒 使用方法 📒📝 使用示例📝 方法📝 属性📝 信号(Signals)📝 槽(Slots)⚓️ 相关链接 ⚓️📖 介绍 📖 QWebEngineView 是 Qt 提供的一个用于呈现 Web 内容的类,基于 Google 的 Chromium 浏览器引擎。它提供了对现…

在线直线度测量仪确保了出厂圆棒无不合格品

在线直线度测量仪确保了出厂圆棒无不合格品 随着生产设备的改进,利用基础材料进行生产的厂家对品质要求也越来越高,其中圆形棒管材的直线度尺寸,也是广受关注,对其进行矫直检测,使其出厂无不合格品。 变抽检为全检 以前…

C 语言每日一题——旋转数组的最小数字

一、题目内容 提供一下该OJ题的链接:旋转数组的最小数字_牛客题霸_牛客网 (nowcoder.com) 二、题目分析 通过示例1可知,我们写代码的目的是在数组中找到一个最大值,并且返回来; 我们很容易的会想到创建一个变量:int…

企业培训系统源码:构建智能、可扩展的学习平台

企业培训系统在现代企业中扮演着至关重要的角色。本文将通过深度解析企业培训系统的源码,介绍如何构建一个智能、可扩展的学习平台,涉及关键技术和代码实例。 1. 技术栈选择与项目初始化 在构建企业培训系统之前,选择适当的技术栈是至关重…

先锋WEB燃气收费系统 Upload.aspx 文件上传漏洞复现

0x01 产品简介 先锋WEB燃气收费系统是一种先进的在线燃气收费解决方案,旨在简化和优化燃气收费的流程和管理。该系统基于Web平台,提供了一系列功能和工具,使燃气公司能够高效地进行收费、账单管理和客户服务。 0x02 漏洞概述 先锋WEB燃气收费系统/AjaxService/Upload.asp…

Windows压缩包的MySQL安装方式

1.下载压缩包 https://cdn.mysql.com//Downloads/MySQL-8.0/mysql-8.0.35-winx64.zip 2.解压压缩包(建议将解压到非C盘,路径不要出现特殊符号) 3.在MySQL主目录下,创建my.ini空文件(先创建一个txt文件,进…

软件测试|selenium 元素无法选择异常的原因及解决

简介 在进行 Web 自动化测试时,使用 Selenium 可能会遇到各种异常情况。其中之一就是 ElementNotSelectableException 异常,该异常通常意味着在尝试选择一个不可选元素时出现了问题。本文将详细介绍这个异常的原因、可能的解决方法,并提供示…

微信小程序开发学习笔记《10》页面导航

微信小程序开发学习笔记《10》页面导航 博主正在学习微信小程序开发,希望记录自己学习过程同时与广大网友共同学习讨论。导航 官方文档 一、介绍 1. 什么是页面导航 页面导航指的是页面之间的相互跳转。例如,浏览器中实现页面导航的方式有如下两种: …

<蓝桥杯软件赛>零基础备赛20周--第14周--BFS

报名明年4月蓝桥杯软件赛的同学们,如果你是大一零基础,目前懵懂中,不知该怎么办,可以看看本博客系列:备赛20周合集 20周的完整安排请点击:20周计划 每周发1个博客,共20周。 在QQ群上交流答疑&am…

C++I/O流——(2)预定义格式的输入/输出(第一节)

归纳编程学习的感悟, 记录奋斗路上的点滴, 希望能帮到一样刻苦的你! 如有不足欢迎指正! 共同学习交流! 🌎欢迎各位→点赞 👍 收藏⭐ 留言​📝 含泪播种的人一定能含笑收获&#xff…

snmp协议配置

引言 SNMP(Simple Network Management Protocol)是一种网络管理协议,用于管理和监控网络设备、操作系统和应用程序。它提供了一组用于检索和修改网络设备配置、监视设备状态和性能的标准化方法。 SNMP 是一个客户端-服务器协议,…