什么是依赖注入——板砖拍Martin Fowler

今天yqj2065用板砖,拍一下Martin Fowler的IoC容器和依赖注入模式》(Inversion of Control Containers and the Dependency Injection pattern[1] )。将我在其他博客中关于他这篇文章的吐槽,条理化。

1.Martin Fowler介绍

Martin Fowler(马丁·福勒),国际著名的OO专家,敏捷开发方法的创始人之一,现为ThoughtWorks公司的首席科学家。在面向对象分析与设计、UML、模式、软件开发方法学、XP、重构等方面,都是世界顶级的专家.他著有几本经典书籍:《分析模式》、《UML精粹》和《重构》等。他是一个低调的人,我个人很喜欢他。但是,该拍他的文章的时候,还是要拍一下。

2.IoC容器和依赖注入模式》的主要错误

(下面是他的文章)

编者语:最近研究IoC,在网上搜索到很多网页推荐阅读Martin Fowler的一篇名叫Inversion of Control Containers and the Dependency Injection pattern的文章。点击到该文章页面便吓了一跳:这什么文章啊,简单一个网页PageRank居然是7!要知道,国内几大门户网站也都还没有到这个值呢!也难怪,Martin Fowler被誉为软件开发教父,他的文章,肯定有N多 人拜读。细细读来,感觉大师确实很厉害,文章条理清晰,论证深入,结论明确!本想将此好文翻译推荐给广大网友,可在网上一搜,早有前人将其翻译成中文了。 这里,我将网上流传的中文版整理后,推荐给广大的编程爱好者,希望大师的精彩讲解能够让你对相关问题有一个透彻的理解。[吹得很高。细细读来,很多地方大师在故作神秘]

摘要:Java社群近来掀起了一阵轻量级容器的热潮,这些容器能够帮助开发者将来自不同项目的组件组装成为一个内聚的应用程序。在它们的背后有着同一个模式,这个模式决定了这些容器进行组件装配的方式。人们用一个大而化之的名字来称呼这个模式:“控制反转”( Inversion of Control,IoC)。在本文中,我将深入探索这个模式的工作原理,给它一个更能描述其特点的名字——“依赖注入”(Dependency Injection),并将其与“服务定位器”(Service Locator)模式作一个比较。不过,这两者之间的差异并不太重要,更重要的是:应该将组件的配置与使用分离开——两个模式的目标都是这个。[为什么他要提到IoC,是因为其他人用IoC容器来吹嘘轻量级容器,而他被带到沟里。当然,他本身也不知道什么是IoC!“依赖注入”(Dependency Injection)和“服务定位器”(Service Locator),两个“模式”的目标是什么?简单地说,Client依赖抽象类型IServer,如何初始化IServer对象?换言之,两个模式都是针对接口编程的使能工具]

在企业级Java的世界里存在一个有趣的现象:有很多人投入很多精力来研究主流J2EE 技术的替代品...。J2EE开发者常遇到的一个问题就是如何组装不同的程序元素:如果web控制器体系结构和数据库接口是由不同的团队所开发的,彼此几乎一无所知,你应该如何让它们配合工作?很多框架尝试过解决这个问题,有几个框架索性朝这个方向发展,提供了更通用的“组装各层组件”的方案。这样的框架通常被称为“轻量级容器”,PicoContainer和Spring都在此列中。

在这些容器背后,一些有趣的设计原则发挥着作用。这些原则已经超越了特定容器的范畴,甚至已经超越了Java平台的范畴。在本文中,我就要初步揭示这些原则。...。


组件和服务

一个简单的例子

为了更好地说明问题,我要引入一个例子。...。【Martin Fowler的这一段文字,比较晦涩和讨厌。可以非常简单明了地说:MovieLister/电影清单 (按照我的一贯做法,用Client或Demo)依赖于接口MovieFinder(我一般用IServer或Animal),要解决的问题就是Demo中要如何初始化Animal的变量(即创建MovieFinder的特定对象),也即 针对接口编程/抽象依赖原则的使能问题。或者说,Demo要依赖Animal——如何创建Animal的对象呢?

在这个例子中,我编写了一个组件,用于提供一份电影清单,清单上列出的影片都是由一位特定的导演执导的。实现这个伟大的功能只需要一个方法:【Demo依赖Animal,他的代码略

class MovieLister...
  ...
    }

你可以看到,这个功能的实现极其简单...。我们真正想要考察的是finder对象,或者说,如何将MovieLister对象与特定的finder对象连接起来。为什么我们对这个问题特别感兴趣?因为我希望上面这个漂亮的moviesDirectedBy方法完全不依赖于影片的实际存储方式。所以,这个方法只能引用一个finder对象,而finder对象则必须知道如何对findAll 方法作出回应。为了帮助读者更清楚地理解,我给finder定义了一个接口:

public interface MovieFinder{
    List findAll();
}

现在,两个对象之间没有什么耦合关系。但是,当我要实际寻找影片时,就必须涉及到MovieFinder的某个具体子类。在这里,我把涉及具体子类的代码放在MovieLister类的构造函数中。

class MovieLister...
    private MovieFinder finder;
    public MovieLister()
    {
        finder = new ColonDelimitedMovieFinder("movies1.txt");
    }

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

图1:在MovieLister 类中直接创建MovieFinder 实例时的依赖关系 

图1展现了这种情况下的依赖关系:MovieLister类既依赖于MovieFinder接口,也依赖于具体的实现类。我们当然希望MovieLister类只依赖于接口,但我们要如何获得一个MovieFinder子类的实例呢? Client要依赖IServer,如何创建IServer的对象呢?

在《Patterns of Enterprise Application Architecture》一书中,我们把这种情况称为插件(plugin):MovieFinder的实现类不是在编译期连入程序之中的,因为我并不知道我的朋友会使用哪个实现类。我们希望MovieLister类能够与MovieFinder的任何实现类配合工作,并且允许在运行期插入具体的实现类,插入动作完全脱离我(原作者)的控制。这里的问题就是:如何设计这个连接过程,使MovieLister类在不知道实现类细节的前提下与其实例协同工作。 

将 这个例子推而广之,在一个真实的系统中,我们可能有数十个服务和组件。在任何时候,我们总可以对使用组件的情形加以抽象,通过接口与具体的组件交流(如果 组件并没有设计一个接口,也可以通过适配器与之交流)。但是,我们希望MovieLister类能够与MovieFinder的任何实现类配合工作,并且允许在运行期插入具体的实现类,插入动作完全脱离我(原作者)的控制。这里的问题就是:如何设计这个连接过程,使MovieLister类在不知道实现类细节的前提下与其实例协同工作。……所以,现在的核心问题就是:如何将这些插件组合成一个应用程序?这正是新生的轻量级容器所面临的主要问题,而 它们解决这个问题的手段无一例外地是控制反转(Inversion of Control)模式

Martin Fowler的这一段文字,罗罗嗦嗦。回想God实验,Client要依赖IServer,工具God创建IServer的对象。我们没有提到插件,没有说“使Client类在不知道实现类细节的前提下与其实例协同工作”,更加没有说“解决这个问题的手段无一例外地是控制反转(Inversion of Control)模式”——God连框架都不是,说什么IoC?它与IoC一毛钱的关系都没有

控制反转

几位轻量级容器的作者曾骄傲地对我说:这些容器非常有用,因为它们实现了控制反转。这样的说辞让我深感迷惑:控制反转是框架所共有的特征,如果仅仅因为使用了控制反转就认为这些轻量级容器与众不同,就好象在说我的轿车是与众不同的,因为它有四个轮

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值