前言
先来一段看似比较装逼的介绍。在软件工程领域,依赖注入(Dependency Injection)是用于实现控制反转(Inversion of Control)的最常见的方式之一。本文主要介绍依赖注入原理和常见的实现方式
依赖注入的作用
控制反转用于解耦,解的究竟是什么?
引用一下 Martin Flower在介绍注入时使用的部分代码说明这个问题。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public class MovieLister{
private MovieFinder finder;
public MovieLister(){
finder = new MovieFinderImpl();
}
public Movie[] moviesDirectedBy(String arg){
List allMovies = finder.findAll();
for(Iterator it = allMovies.iterator(); it.hasNext();){
Movie movie = (Movie)it.next();
if(!movie.getDirector().equals(arg)) it.remove();
}
return (Movie[]) allMovies.toArray(new Movie[allMovies.size()]);
}
...
}
1 | public interface MovieFinder{ |
脑补一下这段代码的功能:
- MovieLister的类来提供需要的电影列表,依赖于MovieFinder对象
- movieDirectedBy方法根据导演名来筛选电影
- MovieFinder接口的实现类MovieFinderImpl负责与数据库交互,搜索电影
目前看来,我们完美的实现了功能!然而,我们都晓得,需求是无时无刻不在改变的~😭,现在我们需要将finder的实现改变(比如增加一个参数)。那我们就需要修改多个类。
这就是依赖注入需要处理的耦合。这种在MovieLister中创建MovieFinderImpl的方式,是的MovieLister不仅仅依赖MovieFinder接口,还依赖于MovieFinderImpl这个实现。这种在一个类中,直接创建另一个累的对象的代码,我们称之为hard init , 它是有毒的:
- 修改实现时,我们需要修改new Object的代码
- 不便于测试,上文中的MovieLister无法单独被测试,其行为和MovieFinderImpl紧紧耦合在一起。
依赖注入的实现方式
其实我们在平常的工作过程中,会经常使用依赖注入,只不过很少注意(反正我是。。。),也不太注意使用依赖注入进行解耦。我们在这里介绍一下依赖注入实现的三种方式。
构造函数注入(Contructor Injection)
在类的外面创建对象,然后通过构造方法传入。1
2
3
4
5
6
7
8// 构造函数注入,MovieLister类就只依赖于我们定义的MovieFinder接口
public class MovieLister{
private MovieFinder finder;
public MovieLister(MovieFinder finder){
this.finder = finder;
}
}
setter方法注入
增加一个setter方法来传入创建好的MovieFinder对象,同样可以避免在MovieFinder中hard init这个对象1
2
3
4
5
6public class MovieLister{
...
public void setFinder(MovieFinder finder){
this.finder = finder;
}
}
接口注入
接口注入使用接口来提供setter方法,其实现方法如下
首先创建一个注入使用的接口1
2
3public interface InjectFinder{
void injectFinder(MovieFinder finder);
}
之后我们让MovieLister实现这个接口1
2
3
4
5
6
7class MovieLister implements InjectFinder{
...
public void injectFinder(MovieFinder finder){
this.finder = finder;
}
...
}
最后我们需要根据不同的框架创建被依赖的MovieFinder的实现。
Java中的注解依赖注入
在java中,使用注解进行依赖注入是最常用的。通过在字段的声明前添加@Inject注解进行标记,来实现依赖对象的自动注入。1
2
3
4
5
6
7
8public class Human{
...
Father father
...
public Human(){
}
}
神奇的@Inject注解,一个注解就自动注入了? 实质上,我们还需要使用依赖注入框架,进行一些配置。比如Dagger。
参考:
author: @ygwang