885191416反转控制容器与依赖注入模式中英文翻译资料

上传人:ni****g 文档编号:431197349 上传时间:2022-08-31 格式:DOC 页数:28 大小:129.50KB
返回 下载 相关 举报
885191416反转控制容器与依赖注入模式中英文翻译资料_第1页
第1页 / 共28页
885191416反转控制容器与依赖注入模式中英文翻译资料_第2页
第2页 / 共28页
885191416反转控制容器与依赖注入模式中英文翻译资料_第3页
第3页 / 共28页
885191416反转控制容器与依赖注入模式中英文翻译资料_第4页
第4页 / 共28页
885191416反转控制容器与依赖注入模式中英文翻译资料_第5页
第5页 / 共28页
点击查看更多>>
资源描述

《885191416反转控制容器与依赖注入模式中英文翻译资料》由会员分享,可在线阅读,更多相关《885191416反转控制容器与依赖注入模式中英文翻译资料(28页珍藏版)》请在金锄头文库上搜索。

1、反转控制容器与依赖注入模式在企业级Java 的世界里存在一个有趣的现象:有很多人投入很多精力来研究主流J2EE 技术的替代品自然,这大多发生在open source 社群。在很大程度上,这可以看作是开发者对主流J2EE 技术的笨重和复杂作出的回应,但其中的确有很多极富创意的想法,的确提供了一些可供选择的方案。J2EE 开发者常遇到的一个问题就是如何组装不同的程序元素:如果web 控制器体系结构和数据库接口是由不同的团队所开发的,彼此几乎一无所知,你应该如何让它们配合工作?很多框架尝试过解决这个问题,有几个框架索性朝这个方向发展,提供了更通用的”组装各层组件”的方案。这样的框架通常被称为”轻量级

2、容器”,PicoContainer 和Spring 都在此列。在这些容器背后,一些有趣的设计原则发挥着作用。这些原则已经超越了特定容器的限制,甚至已经超越了Java 平台的范畴。在本文中,我就要初步揭示这些原则。我使用的范例是Java 代码,但正如我的大多数文章一样,这些原则也同样适用于其它的OO 环境,特别是.NET。组件和服务这样的话题立即将我拖进了一个棘手的术语问题:如何区分”服务”(service)和”组件”(component)?你可以毫不费力地找出关于这两个词定义的长篇大论。有鉴于此,对于这两个遭到了严重滥用的词汇,我将首先说明它们在本文中的用法。在本文中,”组建”指的是一个软件单

3、元,它将被作者无法控制的其他应用程序使用,但后者不能对组件进行修改。也就是说,使用一个组件的应用程序不能修改组件的源代码,但可以通过作者预留的某种途径对其进行扩展,以改变组件的行为。服务和组件有某种相似之处:它们都将被外部的应用程序使用。在我看来,两者之间最大的差异在于:组件是在本地使用的(例如JAR 文件、程序集、DLL、或者源码导入);而服务是要通过同步或异步的远程接口来远程使用的(例如web service、消息系统、RPC,或者socket)。在本文中,我将主要使用“服务”这个词,但文中的大多数逻辑也同样适用于本地组件。实际上,为了方便地访问远程服务,你往往需要某种本地组件框架。不过,

4、“组件或者服务”这样一个词组实在太麻烦了,而且“服务”这个词当下也很流行,所以本文将用”服务”指代这两者。一个简单的例子为了更好地说明问题,我要引入一个例子。和我以前用的所有例子一样,这是一个超级简单的例子:它非常小,小得有点不够真实,但足以帮助你看清其中的道理,而不至于陷入真实例子的泥潭中无法自拔。在这个例子中,我编写了一个组件,用于提供一份电影清单,清单上列出的影片都是由一位特定的导演执导的。实现这个伟大的功能只需要一个方法:class MovieLister. public Movie moviesDirectedBy(String arg) List allMovies = finde

5、r.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 MovieallMovies.size();这个功能的实现极其简单:moviesDirectedBy 方法首先请求finder(影片搜寻者)对象(我们稍后会谈到这个对象)返回后者所知道的所有影片,然后遍历finder 对象

6、返回的清单,并返回其中由特定的某个导演执导的影片。非常简单,不过不必担心,这只是整个例子的脚手架罢了。我们真正想要考察的是finder对象,或者说,如何将MovieLister对象与特定的finder对象连接起来。为什么我们对这个问题特别感兴趣?因为我希望上面这个漂亮的moviesDirectedBy方法完全不依赖于影片的实际存储方式。所以,这个方法只能引用一个finder对象,而finder对象则必须知道如何对findAll 方法作出回应。为了帮助读者更清楚地理解,我给finder定义了一个接口:public interface MovieFinder List findAll();现在,两

7、个对象之间基本没有耦合关系。但是,当我要实际寻找影片时,就必须涉及到MovieFinder 的某个具体子类。在这里,我把”涉及具体子类”的代码放在MovieLister 类的构造子中。class MovieLister. private MovieFinder finder; public MovieLister() finder = new ColonDelimitedMovieFinder(movies1.txt); 这个实现类的名字就说明:我将要从一个逗号分隔的文本文件中获得影片列表。你不必操心具体的实现细节,只要设想这样一个实现类就可以了。现在,如果这个类只由我自己使用,一切都没问题。

8、但是,如果我的朋友叹服于这个精彩的功能,也想使用我的程序,那又会怎么样呢?如果他们也把影片清单保存在一个逗号分隔的文本文件中,并且也把这个文件命名为” movie1.txt ”,那么一切还是没问题。如果他们只是给这个文件改改名,我也可以从一个配置文件获得文件名,这也很容易。但是,如果他们用完全不同的方式例如SQL 数据库、XML 文件、web service,或者另一种格式的文本文件来存储影片清单呢?在这种情况下,我们需要用另一个类来获取数据。由于已经定义了MovieFinder接口,我可以不用修改moviesDirectedBy 方法。但是,我仍然需要通过某种途径获得合适的MovieFind

9、er实现类的实例。图1:”在MovieLister 类中直接创建MovieFinder 实例”时的依赖关系Figure 1: The dependencies using a simple creation in the lister class图1 展现了这种情况下的依赖关系:MovieLister 类既依赖于MovieFinder接口,也依赖于具体的实现类。我们当然希望MovieLister 类只依赖于接口,但我们要如何获得一个MovieFinder子类的实例呢?在Patterns of Enterprise Application Architecture 一书中,我们把这种情况称为”插

10、件”(plugin):MovieFinder的实现类不是在编译期连入程序之中的,因为我并不知道我的朋友会使用哪个实现类。我们希望MovieLister 类能够与MovieFinder的任何实现类配合工作,并且允许在运行期插入具体的实现类,插入动作完全脱离我(原作者)的控制。这里的问题就是:如何设计这个连接过程,使MovieLister 类在不知道实现类细节的前提下与其实例协同工作。将这个例子推而广之,在一个真实的系统中,我们可能有数十个服务和组件。在任何时候,我们总可以对使用组件的情形加以抽象,通过接口与具体的组件交流(如果组件并没有设计一个接口,也可以通过适配器与之交流)。但是,如果我们希望

11、以不同的方式部署这个系统,就需要用插件机制来处理服务之间的交互过程,这样我们才可能在不同的部署方案中使用不同的实现。所以,现在的核心问题就是:如何将这些插件组合成一个应用程序?这正是新生的轻量级容器所面临的主要问题,而它们解决这个问题的手段无一例外地是控制反转(Inversion of Control)模式。控制反转几位轻量级容器的作者曾骄傲地对我说:这些容器非常有用,因为它们实现了”控制反转”。这样的说辞让我深感迷惑:控制反转是框架所共有的特征,如果仅仅因为使用了控制反转就认为这些轻量级容器与众不同,就好像在说”我的轿车是与众不同的,因为它有四个轮子”。问题的关键在于:它们反转了哪方面的控制

12、?我第一次接触到的控制反转针对的是用户界面的主控权。早期的用户界面是完全由应用程序来控制的,你预先设计一系列命令,例如”输入姓名”、”输入地址”等,应用程序逐条输出提示信息,并取回用户的响应。而在图形用户界面环境下,UI 框架将负责执行一个主循环,你的应用程序只需为屏幕的各个区域提供事件处理函数即可。在这里,程序的主控权发生了反转:从应用程序移到了UI框架。对于这些新生的容器,它们反转的是”如何定位插件的具体实现”。在前面那个简单的例子中, MovieLister 类负责定位MovieFinder 的具体实现它直接实例化后者的一个子类。这样一来,MovieFinder 也就不成其为一个插件了,

13、因为它并不是在运行期插入应用程序中的。而这些轻量级容器则使用了更为灵活的办法,只要插件遵循一定的规则,一个独立的组装模块就能够将插件的具体实现”注入”到应用程序中。因此,我想我们需要给这个模式起一个更能说明其特点的名字”控制反转”这个名字太宽泛了,常常让人有些迷惑。与多位IoC 爱好者讨论之后,我们决定将这个模式叫做”依赖注入”(Dependency Injection)。下面,我将开始介绍Dependency Injection 模式的几种不同形式。不过,在此之前,我要首先指出:要消除应用程序对插件实现的依赖,依赖注入并不是唯一的选择,你也可以用Service Locator 模式获得同样的

14、效果。介绍完Dependency Injection 模式之后,我也会谈到Service Locator 模式。依赖注入的几种形式Dependency Injection 模式的基本思想是:用一个单独的对象(装配器)来获得MovieFinder的一个合适的实现,并将其实例赋给MovieLister 类的一个字段。这样一来,我们就得到了图2所示的依赖图:图2:引入依赖注入器之后的依赖关系Figure 2: The dependencies for a Dependency Injector依赖注入的形式主要有三种,我分别将它们叫做构造子注入(Constructor Injection)、设值方法

15、注入(Setter Injection)和接口注入(Interface Injection)。如果读过最近关于IoC 的一些讨论材料,你不难看出:这三种注入形式分别就是type 1 IoC(接口注入)、type 2 IoC(设值方法注入)和type 3 IoC(构造子注入)。我发现数字编号往往比较难记,所以我使用了这里的命名方式。使用PicoContainer 进行构造子注入首先,我要向读者展示如何用一个名为PicoContainer 的轻量级容器完成依赖注入。之所以从这里开始,主要是因为我在ThoughtWorks 公司的几个同事在PicoContainer 的开发社群中非常活跃没错,也可以说是某种偏袒吧。PicoContainer 通过构造子来判断”如何将MovieFinder 实例注入MovieLister 类”。因此,MovieLister 类必须声明一个构造子,并在其中包含所有需要注入的元素:class MovieLister. public MovieLister(MovieFinder finder) this.finder = finder; MovieFinder 实例本身也将由PicoContainer来管理,因此文本文件的名字也可以由容器注入:class Colon

展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 大杂烩/其它

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号