依赖注入 (Dependency Injection)

前言

乍听依赖注入,似乎是什么很高级的东西,其实不然,其只是名字高大上,我们实际上在不知不觉间就经常用它了。(本文只记录普遍性结论,不在某个框架下讨论)

什么是依赖注入

依赖注入是软件工程中的一个名词,不止存在于JAVA语言中,在多种语言中都有体现。本文只针对JAVA语言举例。
从软件工程的角度来看,依赖注入(Dependency Injection,简称DI)是一种设计模式,旨在减少软件组件之间的紧耦合,并促进代码的重用性和可测试性。在软件工程实践中,依赖注入提供了一种方法来管理对象之间的依赖关系,使得系统结构更加清晰和模块化。
以上叙述比较书面化,简单来说,依赖注入是一种组件解耦的思想。

引入

举一个简单的例子,一个小孩想让一只猫唱歌。先看一组比较“传统”的代码:

class Cat {
    public void sing(){
    	System.out.println("我是一只猫,快乐的猫猫!!");
    }
}

class Person {
    private final Cat cat = new Cat();
    public void catTosing(){
    	cat.sing();
    }
}

public class Main {
    public static void main(String[] args) {
        Person persion = new Person ();
        persion.catTosing();
    }
}

这样的代码可能有些奇怪,但它也完成了我们的要求。对该段代码进行分析,我们发现,代码有个很大的问题,即小孩和猫绑定了,当我们新建一个小孩的实例的时候,在其内部就同时新建一个猫的实例,小孩和猫两个组件完全绑定在一起了。
但是小孩可以随便找一只猫来叫,猫猫的叫声有多种选择,比如喵喵、咪咪、呵呵。那么我们应该要把猫和小孩“解绑”,使得小孩可以随意找一只猫来叫。我们最通常的写法应该是这样的:

abstract class Cat {
    public void sing();
}
class DragonLi extends Cat {
	@Override
	 public void sing(){
    	System.out.println("嗷呜");
    }
}
class Person {
    private  Cat cat ;
    public Person(Cat cat){
    	this.cat = cat;
    } 
    public void catTosing(){
    	cat.sing();
    }
}

public class Main {
    public static void main(String[] args) {
    	Cat cat = new DragonLi(); 
        Person persion = new Person (cat );
        persion.catTosing();
    }
}

以上代码事实上就完成了一次依赖注入,在以上代码中,Person组件和Cat组件是解耦的,Person 类不再需要关心 Cat 是如何实现的,只要继承了 Cat ,你可以传入任何的类进去。

提出背景

在传统的软件开发中,一个对象(类、模块等)可能需要依赖于其他对象才能完成其功能。如果这些依赖关系是在对象内部创建或硬编码的,那么这些对象就会变得难以独立测试和复用。此外,当需要更改依赖项的行为时(例如,小孩想换一只会叫哥哥的猫),整个系统可能都需要进行大规模的修改。

如何工作
  • 构造函数注入(Constructor Injection):依赖项作为构造函数的参数传递给对象。这是最常用的一种方式,因为它确保了依赖项在对象创建时已经被正确设置。比如,我们举的例子。

    class Person {
        private final Cat cat ;
        public Person(Cat cat){//
        	this.cat = cat;
        } 
        public void catTosing(){
        	cat.sing();
        }
    }
    
  • 属性注入(Property Injection):依赖项通过对象的属性或字段设置。这种方式较为灵活,但可能会导致对象状态的不一致性。举例,我们不在构造函数中给cat赋值了,而是单独写一个setCat方法。

    class Person {
    	private  Cat cat ;
    	public void setCat(Cat cat){//
        	this.cat = cat;
        }
        public void catTosing(){
    		cat.sing();
    	}
    }
    
  • 方法注入(Method Injection):依赖项通过方法调用时作为参数传递。这种形式不太常见,通常用于某些特定场景下的依赖,通常用于临时性的或特定上下文的依赖。举例,我们干脆不在Person中保留cat这个属性了,而是直接修改catTosing方法。

    class Person {
        public void catTosing(Cat cat){//
        	cat.sing();
        }
    }
    

好处

依赖注入带来了一系列的好处,包括但不限于:

  • 降低耦合度:对象之间不再直接依赖彼此的实现细节,而是依赖于抽象接口或契约。
  • 提高可测试性:可以更容易地为测试提供模拟对象或存根,以隔离待测对象的外部影响。
  • 增加灵活性:依赖项可以在运行时动态配置,而无需重新编译或修改代码。

应用

在现代的软件开发中,依赖注入经常与IoC(Inversion of Control)容器一起使用,这种容器负责管理应用程序中的对象及其依赖关系,自动执行依赖注入的过程。例如Java中的Spring框架,.NET中的Autofac等。这些工具提供了自动化依赖注入的能力,使得开发者可以专注于业务逻辑,而不是依赖关系的管理。
如果单纯对某框架而言,依赖注入可能有不同角度的定义,就比如Spring框架,那就不是这篇文章要讨论的了。

  • 18
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值