文档章节

八一八 依赖注入

geek_loser
 geek_loser
发布于 2017/07/05 16:45
字数 1129
阅读 2
收藏 0

0. 前言

在软件工程领域,依赖注入(Dependency Injection)是用于实现控制反转(Inversion of Control)的最常见的方式之一。本文主要介绍依赖注入原理和常见的实现方式,重点在于介绍这种年轻的设计模式的适用场景及优势。

1. 为什么需要依赖注入

控制反转用于解耦,解的究竟是谁和谁的耦?这是我在最初了解依赖注入时候产生的第一个问题。

下面我引用Martin Flower在解释介绍注入时使用的一部分代码来说明这个问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public 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
2
3
public interface MovieFinder {
    List findAll();
}

我们创建了一个名为MovieLister的类来提供需要的电影列表,它moviesDirectedBy方法提供根据导演名来搜索电影的方式。真正负责搜索电影的是实现了MovieFinder接口的MovieFinderImpl,我们的MovieLister类在构造函数中创建了一个MovieFinderImpl的对象。

目前看来,一切都不错。但是,当我们希望修改finder,将finder替换为一种新的实现时(比如为MovieFinder增加一个参数表明Movie数据的来源是哪个数据库),我们不仅需要修改MovieFinderImpl类,还需要修改我们MovieLister中创建MovieFinderImpl的代码。

这就是依赖注入要处理的耦合。这种在MovieLister中创建MovieFinderImpl的方式,使得MovieLister不仅仅依赖于MovieFinder这个接口,它还依赖于MovieListImpl这个实现。 这种在一个类中直接创建另一个类的对象的代码,和硬编码(hard-coded strings)以及硬编码的数字(magic numbers)一样,是一种导致耦合的坏味道,我们可以把这种坏味道称为硬初始化(hard init)。同时,我们也应该像记住硬编码一样记住,new(对象创建)是有毒的。

Hard Init带来的主要坏处有两个方面:1)上文所述的修改其实现时,需要修改创建处的代码;2)不便于测试,这种方式创建的类(上文中的MovieLister)无法单独被测试,其行为和MovieFinderImpl紧紧耦合在一起,同时,也会导致代码的可读性问题(“如果一段代码不便于测试,那么它一定不便于阅读。”)。

2. 依赖注入的实现方式

依赖注入其实并不神奇,我们日常的代码中很多都用到了依赖注入,但很少注意到它,也很少主动使用依赖注入进行解耦。这里我们简单介绍一下赖注入实现三种的方式。

2.1 构造函数注入(Contructor Injection)

这是我认为的最简单的依赖注入方式,我们修改一下上面代码中MovieList的构造函数,使得MovieFinderImpl的实现在MovieLister类之外创建。这样,MovieLister就只依赖于我们定义的MovieFinder接口,而不依赖于MovieFinder的实现了。

1
2
3
4
5
6
7
8
public class MovieLister {
    private MovieFinder finder;

    public MovieLister(MovieFinder finder) {
        this.finder = finder;
    }
    ...
}

 

2.2 setter注入

类似的,我们可以增加一个setter函数来传入创建好的MovieFinder对象,这样同样可以避免在MovieFinder中hard init这个对象。

1
2
3
4
5
6
public class MovieLister {
    s...
    public void setFinder(MovieFinder finder) {
        this.finder = finder;
    }
}

 

2.3 接口注入

接口注入使用接口来提供setter方法,其实现方式如下。
首先要创建一个注入使用的接口。

1
2
3
public interface InjectFinder {
    void injectFinder(MovieFinder finder);
}


之后,我们让MovieLister实现这个接口。

1
2
3
4
5
6
7
class MovieLister implements InjectFinder {
    ...
    public void injectFinder(MovieFinder finder) {
      this.finder = finder;
    }
    ...
}


最后,我们需要根据不同的框架创建被依赖的MovieFinder的实现。

 

3. 最后

依赖注入降低了依赖和被依赖类型间的耦合,在修改被依赖的类型实现时,不需要修改依赖类型的实现,同时,对于依赖类型的测试,可以更方便的使用mocking object替代原有的被依赖类型,以达到对依赖对象独立进行单元测试的目的。

最后需要注意的是,依赖注入只是控制反转的一种实现方式。控制反转还有一种常见的实现方式称为依赖查找。

© 著作权归作者所有

geek_loser
粉丝 3
博文 74
码字总数 18849
作品 0
朝阳
私信 提问
tech| 技术分享: 加解密那些事儿

date: 2018-7-10 15:06:46 title: tech| 技术分享: 加解密那些事儿 description: 浅谈加解密的技术栈, 详解常见的几种用法, 八一八有趣的人和事 关于加解密, 需要补充的基础知识: https权威指...

daydaygo
2018/08/18
0
0
Windows Container技术的一些疑惑

Windows Container还是不太好搞,目前看不到细节讨论,很不爽,暂时也没环境试验啊。 就那一坨注册表,随便安装个什么东西都牵扯到他,势必得把注册表也做成AuFS那样的分层次的存储,以便共享...

Q_J
2016/07/08
30
0
【Java小收获】使用Collection对ArrayList排序

正文之前 毕业论文肝到14000实在是肝不动了,必须开点新模块才能走得动,不然没法搞。所以最近在琢磨离散化这个东西。主要是参考的这个文献 正文 好吧,这个只是个Part,做个笔记而已,不必较...

HustWolf
2018/05/12
0
0
SSL 和黑客们哭晕在厕所...

经 过“Heartbleed”(心脏出血)漏洞的洗礼后,SSL安全问题就一直牵动着国内安全研究者的神经,昨天SSLv3漏洞新闻一出,差点又乱作一团 (有企业小伙伴跟乌云君说差点准备红牛泡面准备加班了...

oschina
2014/10/16
12.1K
23
用 yii 框架 10 分钟开发 blog 系统?

date: 2017-10-31 01:19:46 title: 用 yii 框架 10 分钟开发 blog 系统? 因为最近可能工作需要, 又要开始使用 yii 框架. 想想都快 2 年没碰了, 最近一次接触到, 还是整理自己的 wiki, 关于 ...

daydaygo
2017/11/01
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Angular 英雄编辑器

应用程序现在有了基本的标题。 接下来你要创建一个新的组件来显示英雄信息并且把这个组件放到应用程序的外壳里去。 创建英雄组件 使用 Angular CLI 创建一个名为 heroes 的新组件。 ng gener...

honeymoose
今天
5
0
Kernel DMA

为什么会有DMA(直接内存访问)?我们知道通常情况下,内存数据跟外设之间的通信是通过cpu来传递的。cpu运行io指令将数据从内存拷贝到外设的io端口,或者从外设的io端口拷贝到内存。由于外设...

yepanl
今天
6
0
hive

一、hive的定义: Hive是一个SQL解析引擎,将SQL语句转译成MR Job,然后再在Hadoop平台上运行,达到快速开发的目的 Hive中的表是纯逻辑表,就只是表的定义,即表的元数据。本质就是Hadoop的目...

霉男纸
今天
5
0
二、Spring Cloud—Eureka(Greenwich.SR1)

注:本系列文章所用工具及版本如下:开发工具(IDEA 2018.3.5),Spring Boot(2.1.3.RELEASE),Spring Cloud(Greenwich.SR1),Maven(3.6.0),JDK(1.8) Eureka: Eureka是Netflix开发...

倪伟伟
昨天
15
0
eclipse常用插件

amaterasUML https://takezoe.github.io/amateras-update-site/ https://github.com/takezoe/amateras-modeler modelGoon https://www.cnblogs.com/aademeng/articles/6890266.html......

大头鬼_yc
昨天
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部