名词解释

依赖倒置:主要是为了消除模块间的依赖关系,其实分离接口的方法也可以起到减少依赖的关系,但是效果不是很好。

dip_ioc3.gif

Java语言提供了纯粹的接口类,这种接口类不包括任何实现代码,可以更好地隔离两个模块。C++语言中虽然没有定义这种纯粹的接口类,但所有成员函数都是纯虚函数的抽象类也不包含任何实现代码,可以起到类似于Java接口类的作用。为了和上一节中提到的简单接口相区别,本文后面将把基于Java 接口类或C++抽象类定义的接口称为抽象接口。依赖倒置原则就是建立在抽象接口的基础上的。Robert Martin这样描述依赖倒置原则[Martin 1996]:

A. 上层模块不应该依赖于下层模块,它们共同依赖于一个抽象。

B. 抽象不能依赖于具象,具象依赖于抽象。

其含义是:为了消解两个模块间的依赖关系,应该在两个模块之间定义一个抽象接口,上层模块调用抽象接口定义的函数,下层模块实现该接口。如图 3所示,对于上一节的例子,我们可以定义两个抽象类Reader和Writer作为抽象接口,其中的Read()和Write()函数都是纯虚函数,而具体的KeyboardReader和PrinterWriter类实现了这些接口。当应用程序调用Read()和Write()函数时,由于多态性机制的作用,实际调用的是具体的KeyboardReader和PrinterWriter类中的实现。因此,抽象接口隔离了应用程序和类库中的具体类,使它们之间没有直接的耦合关系,可以独立地扩展或重用。例如,我们可以用类似的方法实现FileReader或DiskWriter类,应用程序既可以根据需要选择从键盘或文件输入,也可以选择向打印机或磁盘输出,甚至同时完成多种不同的输入、输出任务。由此可以总结出,这种通过抽象接口消解应用程序和类库之间依赖关系的做法具有以下特点:

1. 应用程序调用类库的抽象接口,依赖于类库的抽象接口;具体的实现类派生自类库的抽象接口,也依赖于类库的抽象接口。

2. 应用程序和具体的类库实现完全独立,相互之间没有直接的依赖关系,只要保持接口类的稳定,应用程序和类库的具体实现都可以独立地发生变化。

图 4

3. 类库完全可以独立重用,应用程序可以和任何一个实现了相同抽象接口的类库协同工作。

一般情况下,由于类库的设计者并不知道应用程序会如何使用类库,抽象接口大多由类库设计者根据自己设想的典型使用模式总结出来,并保留一定的灵活度,以提供给应用程序的开发者使用。

但还有另外一种情况。图 4是Martin Fowler在《Reducing Coupling》一文中使用的一个例子[Fowler 2001]。其中,Domain包要使用数据库包,即Domain包依赖于数据库包。为了隔离Domain包和数据库包,可以引入一个Mapper包。如果在特定的情况下,我们希望Domain包能够被多次重用,而Mapper包可以随时变化,那么,我们就必须防止Domain包过分地依赖于Mapper包。这时,可以由 Domain包的设计者总结出自己需要的抽象接口(如Store),而由Mapper包的设计者来实现该抽象接口。这样一来,无论是在接口层面,还是在实现层面,依赖关系都完全颠倒过来了。


控制反转

前面描述的是应用程序和类库之间的依赖关系。如果我们开发的不是类库,而是框架系统,依赖关系就会更强烈一点。那么,该如何消解框架和应用程序之间的依赖关系呢?

posted on 2007-12-07 09:13  墙外行人  阅读(453)  评论(0)    收藏  举报

导航