前言
先来一段看似比较装逼的介绍。在软件工程领域,依赖注入(Dependency Injection)是用于实现控制反转(Inversion of Control)的最常见的方式之一。本文主要介绍依赖注入原理和常见的实现方式
依赖注入的作用
控制反转用于解耦,解的究竟是什么?
引用一下 Martin Flower在介绍注入时使用的部分代码说明这个问题。
|
|
脑补一下这段代码的功能:
- MovieLister的类来提供需要的电影列表,依赖于MovieFinder对象
- movieDirectedBy方法根据导演名来筛选电影
- MovieFinder接口的实现类MovieFinderImpl负责与数据库交互,搜索电影
目前看来,我们完美的实现了功能!然而,我们都晓得,需求是无时无刻不在改变的~😭,现在我们需要将finder的实现改变(比如增加一个参数)。那我们就需要修改多个类。
这就是依赖注入需要处理的耦合。这种在MovieLister中创建MovieFinderImpl的方式,是的MovieLister不仅仅依赖MovieFinder接口,还依赖于MovieFinderImpl这个实现。这种在一个类中,直接创建另一个累的对象的代码,我们称之为hard init , 它是有毒的:
- 修改实现时,我们需要修改new Object的代码
- 不便于测试,上文中的MovieLister无法单独被测试,其行为和MovieFinderImpl紧紧耦合在一起。
依赖注入的实现方式
其实我们在平常的工作过程中,会经常使用依赖注入,只不过很少注意(反正我是。。。),也不太注意使用依赖注入进行解耦。我们在这里介绍一下依赖注入实现的三种方式。
构造函数注入(Contructor Injection)
在类的外面创建对象,然后通过构造方法传入。
setter方法注入
增加一个setter方法来传入创建好的MovieFinder对象,同样可以避免在MovieFinder中hard init这个对象
接口注入
接口注入使用接口来提供setter方法,其实现方法如下
首先创建一个注入使用的接口
之后我们让MovieLister实现这个接口
最后我们需要根据不同的框架创建被依赖的MovieFinder的实现。
Java中的注解依赖注入
在java中,使用注解进行依赖注入是最常用的。通过在字段的声明前添加@Inject注解进行标记,来实现依赖对象的自动注入。
神奇的@Inject注解,一个注解就自动注入了? 实质上,我们还需要使用依赖注入框架,进行一些配置。比如Dagger。
参考: