设计模式 学习笔记 之十二

第15章 Adapter模式

Adapter模式是一种简单的模式。当我们需要使用一个现成的类,但它的接口又跟要求的接口有出入时,我们就使用Adapter模式把这个现成的类适配到目标接口。我们下面来看一个具体的例子。

假设我们在开发一个2维图形程序。在这个程序中,需要乃至若干形状类,如点、直线和矩形。为了给这些开关类建模,我们设计了一个Shape接口,如下所示。

clip_image002

现在新的需求来了。我们希望加入对圆形的支持,这就是说,我们要在Shape class hierarchy中加入Circle类并实现它。不过幸运的是,公司中已经有另外的同事开发出了一个名为XXCircle的类,它能够处理圆形的相关操作。但是,它的方法的命名跟Shape接口所要求的有点出入。

clip_image004

为了能使用这个现成的类,但同时又满足Shape接口,我们需要使XXCircle类适配到Shape接口,我们为此引入一个适配器类,就像下面这样。

clip_image006

这就是Adapter模式。Adapter模式让我们不必担心接口的匹配问题。如果有一个现成的类,它满足我们的需要但接口不正确,我们总是使用Adapter模式让它适配到正确的接口。

第16章 Bridge模式

在本章中我们要学习Bridge模式。我们关注的重点是如何从问题域推导出Bridge模式。正如之前我们反复强调的,使用共性变性分析,我们可以获得优良的设计。Bridge模式也是应该共性变性分析的一个佐证。我们通过一个例子来学习。

我们继续开发前一章提到的2维图形程序。假设目前我们得到了如下图所示的设计:

clip_image008

现在又来了一个新的需求。原先的系统是使用V1版本的驱动程序,现在驱动程序供应商开发出了V2版本的驱动程序,因此我们的2维图形程序需要同时支持这两个版本的驱动程序。

怎么解决这个问题?一个比较容易想到的办法是:从Point,Line等类中继续派生出子类,让子类使用不同的驱动程序。这样得到的设计如下图所示。

clip_image010

这是个优良的设计吗?至少从直觉上看不是,因为这个class hierarchy中的类似乎有点多。我们不妨假设一下,如果在Shape中再引入一个新的图形,如正方形,那么就会要求引入3个子类:Square,V1Square和V2Square。另一方面,如果以后要求同时支持V1、V2和V3版本的驱动程序,那么已有的全部图形类都要再扩展出一个V3Shape类来。类爆炸的问题已经显现了!

这个低劣设计的根源在哪里?我们来看一下,在已有的Shape class hierarchy中,共性是形状类,变性是不同的形状。现在,为了应对新的需求变化,设计者在已有的Shape class hierarchy中叠加上另一个共性变性分析,这一次共性是使用驱动程序,变性是使用的驱动程序的版本不同。在一个class hierarchy中利用继承关系叠加上两个不相关的共性变性分析是非常错误的,它的结果往往就是引起类爆炸!

那么怎么解决?其实上,我们已经分析出了问题的关键:把两个共性变性分析使用继承关系叠加到同一个class hierarchy中。因此,解决它的办法就是,把两个共性变性分析封装到两个class hierarchy中,然后使用委派关系(而非继承关系)把这两个共性变性分析关联起来,就像下面这样。

clip_image012

这样得到的新设计比前面的设计“清爽”多了。让Shape class hierarchy与Driver class hierarchy隔离开来,使得它们不再杂糅在同一个class hierarchy中,这样Shape和Driver都可以灵活地变化了。当然,由于它们之间存在委派关系,因此不可能完全互不影响,但是,比起继承那样的强耦合关系,使用委派使得Shape和Driver相互之间的影响大大降低了。

这个新的设计体现的正是Bridge模式。Bridge模式的要义就是使用“委派关系”让两个class hierarchy松散耦合,使得它们彼此之间的影响最小化。

实际上,对驱动程序的依赖都是Bridge模式的体现。在开发驱动程序时,一般都会设计出一个抽象驱动程序层(ADI),而不同的驱动程序都实现这个ADI。应用程序则通过委派关系来使用驱动程序,这样应用程序与驱动程序程序是松散耦合的,应用程序和驱动程序可以独立开发,彼此在多数情况下不会互相影响。

clip_image014

posted @ 2011-05-15 18:47  李嘉 (Justin)  阅读(198)  评论(0编辑  收藏  举报