对依赖注入技术的初学指导

来源:互联网 发布:淘宝玩乐收藏类目在哪 编辑:程序博客网 时间:2024/04/30 01:13

依赖注入技术(Dependency Injection)被世人所认知和使用有一段时间了。近来,越来越多的人开始使用它的原理来设计、开发、和单元测试Java程序。一些很优秀很有意思的开源框架也由此开发出来,比如SpringFramework和Google Guice等等。译者翻译的这篇近两年前的文章,对于人们了解和学习Dependency Injection的基本原理应该是有裨益的。

DependencyInjection目前在业界的直译(依赖注入)而言,译者认为不明了。所以以下就还一直使用英文。另请注意,这项技术并不是将依赖性进行注入,而是一些功能和资源的注入,请读者不要望文生义。

原文:

范畴
这篇文章从较高的层次来一览DependencyInjection(DI)技术。它的目的是在如何使用几种DI容器的背景下,把DI技术概念性地介绍给初学者。

是DI还是IOC(反向控制)?
当前的许多资料通常将DI和IOC混为一谈。就我个人而言,DI应该是一个更具体的名字。我能想像在许多情况下使用IOC的时候,不见得最后的模式就是以解决依赖性为重心(比如从一个控制台应用程序到一个窗口事件循环)。但是,如果你更习惯IOC,你基本上可以将以下文章里的DI换成IOC来读。IOC有多种类别,像一类,二类,三类。这些类别间的不同不
在本文的讨论范围之内。
另请注意我在本文里大量使用了服务端(Service)词。请不要将它混淆为以服务为导向的框架(SOA)。我这里的意思只是用用户端来代表用户端组件,服务端来代表服务端组件。

DI的简要介绍

简要介绍

情境一
假设你在一个将出差当成家常便饭的公司工作。通常来说,你乘飞机来旅行。每次你赶飞机时,总是要安排taxi。你认识航空公司订票的人员,出租车公司安排taxi送你到机场的人员。你知道他们的电话,你知道通常订票的谈话内容。你的典型的旅行步骤如:

  • 决定目的地,和希望到达时间;
  • 给航空公司打电话,将必要的旅行信息传达出去,用以订票
  • 给出租公司打电话,要求taxi从你住地送你到机场去赶某一具体航班(出租公司可能需要联络航空公司来得到航班的起飞时间表,机场信息,并计算从你住地至机场的的具体时间,及相应的到达你住地的时间)
  • 获取机票,赶上taxi,开始出差之旅

如果现在你的公司突然更换原先订票的经纪以及相应的交流手段,你可能被迫进入重新熟悉的境地:

  • 新的经纪公司,他们的交流方式(比如说,如果新的经纪通过互联网来做生意,而不是原来的电话)
  • 用以成交的典型的谈话方式的次序(是数据,而不再是声音)

不仅仅是你,很可能你的许多同事也要对此变化进行适应。适应的过程往往要花上可观的时间。

情境二
现在让我们来假设整个程序有一点点不同。你们公司有一个行政部门。当你需要出差旅行的时候,行政部门的互动电话系统会给你打电话(事实上是将你和订票经纪公司挂起钩来)。在电话上,你只需回答特定的一套问题,来讲出你的目的地和需要的到达时间。机票订票系统是专为你们设计的,出租公司将计划好taxi的时间,同时,机票也会给你送上门来。

如果现在订票的经纪更换了的话,行政部门会知会这个变更,也许他们会相应调整订票经纪的工作流程。互动的电话系统可以重新程序化,以便于和经纪在互联网上沟通。但是,你和你的同事们不需要有任何的重新适应过程。你仍旧只需照先前的程序走就行了(所有的调整都由行政部门去做了)

依赖注入DependencyInjection)

在上述两个情境中,你是客户,你依赖于经纪提供的服务。但是,情境二有些不同:

  • 你不需要知道订票经纪人和电话 - 必要的话,公司行政部门会打电话给你
  • 你不需要知道行政部门和订票经纪的交流程序和方式,虽然你知道如何与行政部门一个特定的交流方式
  • 你不需要作出任何调整,即使当给你提供服务的经纪有变更的时候

这就是现实生活中的依赖性注入(DI)。这种方式看上去省不了你什么,但是当你把它应用到一个大公司里去时,他的节省是很可观的。

在软件领域里的DI

软件的组件(用户端)通常是一整套相互作用的组件中的一部分,这一整套组件又会依赖于一些其他的组件(服务端)来完成它们的预定目标。在许多情况下,它们需要知道和哪些组件打交道,在哪里可以找到那些组件,以及如何和那些组件交流。如果获取到这些组件(服务端)的方式改变时,这些变化可能迫使大量用户端的组件作出调整。

有鉴于此,把代码结构化的一个方法是,将找到和实例化服务端组件的逻辑嵌入到用户端的组件里。另一个方法是将客户端对服务端的依赖性声明,让一些‘外在’的代码来承担找到和实例化服务端组件的任务,并提供相关的服务端的引用(reference)给客户端。后一种方式里,当寻找外在依赖性的方式变更时,客户端的代码通常不需要调整。这种实施的方法被认为是DI,前面提到过的那些’外在‘的代码可能是自己亲自写的,也可能是按一些现成的DI框架来实施的。

那些试着将上述情况在设计术语中得到印证的人会发现他们之间的可比性适用于三个不同的术语 - DI,工厂(Factory),和程序接口(不是具体的实施)。在这个比较中,这三个术语通常被用到(不一定同时用到)。

一直到几年前,传统的方法总是将接口和实施分离开来。工厂设计模式甚至允许将复杂的实例化隐藏起来。但是,找到服务端组件的机制就交给客户端去做了。而且,部分的服务端组件需要晓得其内部各组件之间的依赖关系,在实例化的过程中暗含的次序问题,以及跟踪和管理他们的生命周期。

举例来说,J2EE中使用了JNDI来标准化找寻对象引用的机制。这样的实施在一些简单的,没有JNDI的环境中就会迫使客户端的代码作出调整。

DI的使用要求我们在写软件时,把依赖性搞清楚,让DI框架里的容器替我们去做那些将服务端部件实例化、初始化、次序化、然后提供用户端需要的服务端的引用的复杂工作。

DI框架举例

现在有不少对开发人员有帮助的框架。我发现确实有用的有:

  • Spring框架:一个相当大的框架,提供包括DI在内的很多功能
  • Pico框架:一个很小,但是集中于DI容器的框架
  • HiveMind:另一个DI容器的框架
  • XWork:一个非常有效地利用DI的,主要基于命令模式的框架。虽然它是独立存在的,但它通常和Webwork结合起来用

在下面的章节里,我们将取一种情境,先不用DI来实施一次,再用上述提到的每一种DI框架来实施。这样,我们就能体会到如何使用这些框架。这里,我尽量少用代码,而且这些代码的主要重心也在DI相关的功能上。

原创粉丝点击