https://blog.csdn.net/coderder/article/details/51897721

  1. 前言

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

  2. 为什么需要依赖注入

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

下面我引用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紧紧耦合在一起,同时,也会导致代码的可读性问题(“如果一段代码不便于测试,那么它一定不便于阅读。”)。

  1. 依赖注入的实现方式

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

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替代原有的被依赖类型,以达到对依赖对象独立进行单元测试的目的。

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

参考

Inversion of Control Containers and the Dependency Injection pattern

How to Think About the “new” Operator with Respect to Unit Testing

依赖注入

java 依赖注入的更多相关文章

  1. java依赖注入(injection)

    和SpringSource分别通过其开源项目Guice及Spring Framework提供了依赖注入的功能.然而直到现在开发者也没有一种标准的.独立于供应商的方式从而无需修改其源文件就能在这些框架之 ...

  2. Java 依赖注入标准(JSR-330)简介

    作者:88250 ,Vanessa 时间:2009 年 11 月 19 日      Java 依赖注入标准(JSR-330,Dependency Injection for Java)1.0 规范已 ...

  3. java依赖注入

    接口的作用 1.在spide中创建一个私有接口 private Downloadable downlaodable 覆盖set get 方法 创建一个方法  Public Page down load ...

  4. Java依赖注入方式

    pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w ...

  5. 开涛spring3(12.2) - 零配置 之 12.2 注解实现Bean依赖注入

    12.2  注解实现Bean依赖注入 12.2.1  概述 注解实现Bean配置主要用来进行如依赖注入.生命周期回调方法定义等,不能消除XML文件中的Bean元数据定义,且基于XML配置中的依赖注入的 ...

  6. 注解实现Bean依赖注入

    12.2.1  概述 注解实现Bean配置主要用来进行如依赖注入.生命周期回调方法定义等,不能消除XML文件中的Bean元数据定义,且基于XML配置中的依赖注入的数据将覆盖基于注解配置中的依赖注入的数 ...

  7. Android 和 Dagger 2 中的依赖注入

    原文:Dependency Injection in Android with Dagger 2 作者:Joe Howard 译者:kmyhy 在现代开发团队中到处充斥着"你一定要用依赖注入 ...

  8. Objection, 一个轻量级的Objective-C依赖注入框架

    简介 项目主页:https://github.com/atomicobject/objection 实例下载: https://github.com/ios122/ios122 Objection 是 ...

  9. Java之控制反转和依赖注入

    1.简介 依赖注入和控制反转,目的是为了使类与类之间解耦合,提高系统的可扩展性和可维护性,下面通过一个例子来引入这一概念. 2.案例 1)一般情况下的类耦合 Main.java public clas ...

随机推荐

  1. i.MX RT1010之FlexIO模拟SSI外设

    恩智浦的i.MX RT1010是跨界处理器产品,作为i.MX RT跨界MCU系列的一个新的切入点,i.MX RT1010是成本最低的LQFP封装方式与i.MX RT系列产品一贯的高性能和易用性的结合产 ...

  2. .NET基础拾遗(1)类型语法基础和内存管理基础【转】

    http://www.cnblogs.com/edisonchou/p/4787775.html Index : (1)类型语法.内存管理和垃圾回收基础 (2)面向对象的实现和异常的处理 (3)字符串 ...

  3. socketserver 模块简介

    一.socketserver模块简介 socketserver模块简化了网络编程,模块下有五个服务类:BaseServer.TCPServer.UDPServer.UnixStreamServer.U ...

  4. 事件和方法的区别,以input框的blur事件为例

    1. 我们在原生的js中学到的事件 onblur 2. 使input框失去焦点的方法blur 3. jquery中的方法blur 是当input框失去焦点时触发的回调 三者是不相同的 事件:指的是一个 ...

  5. Git创建合并和删除分支

    创建并切换分支 git checkout命令加上-b参数表示创建并切换分支,以下为创建并切换到dev分支: 相当于先通过 $ git branch dev 命令创建dev分支,然后 $ git che ...

  6. Springboot项目搭建(1)-创建,整合mysql/oracle,druid配置,简单的CRUD

    源码地址:https://github.com/VioletSY/article-base 1:创建一个基本项目:https://blog.csdn.net/mousede/article/detai ...

  7. 【C语言】创建一个函数,判断某一正整数是否为水仙花数,并调用这个函数找出1000以内所有水仙花数

    #include <stdio.h> int fun(int x) { int a, b, c; a = x / ; b = x % / ; c = x % ; if (x == a * ...

  8. android studio :Timeout waiting to lock daemon addresses registry

    一.开发中 android studio 突然遇到下面的错误提示: Error:Timeout waiting to lock daemon addresses registry. It is cur ...

  9. JavaEE实战——XML文档DOM、SAX、STAX解析方式详解

    原 JavaEE实战--XML文档DOM.SAX.STAX解析方式详解 2016年06月22日 23:10:35 李春春_ 阅读数:3445 标签: DOMSAXSTAXJAXPXML Pull 更多 ...

  10. Spark性能调优-基础篇

    前言 在大数据计算领域,Spark已经成为了越来越流行.越来越受欢迎的计算平台之一.Spark的功能涵盖了大数据领域的离线批处理.SQL类处理.流式/实时计算.机器学习.图计算等各种不同类型的计算操作 ...