适配器模式和外观模式有些相似,甚至其实现方式也相似,不同的只是他们的侧重点。
1. 适配器模式
适配器模式,我想用处应该非常广泛,特别是对于环境多变的场合,其定义如下:
将一个类的接口,转换成客户希望的另一个接口。适配器让原本不兼容的类可以合作无间。
这解决了本章开篇的那个需求:“将一个方块放入一个圆洞中!”。
也就是说,适配器通过接口转换解决了兼容性问题。那么他是通过什么方式实现的呢?
在HeadFirst中,作者介绍了两种实现方式:类适配器、对象适配器。
类适配器使用多继承的方式实现,及同时继承客户期望的类和需要被适配的类,并重载客户类中的接口,使其应用被适配类的接口。其UML图如下(来自wikipedia):
而对象适配器则使用组合的方式应用被适配者的接口,其UML图如下(同样来自wikipedia):
请注意,这种方案中的Adaptor必须与客户类接口一致,我们可以通过继承客户类并重载其方法的方案来实现这个效果。
下面我来举两个例子分别说明这两种实现方案:
这两个例子的场景是这样的:原本我们有一个矩形类:ORect,使用左上角 (topLeft) 和右下角 (bottomRight) 两个点 (OPoint) 来记录,ORect 包含两个方法:setRect(OPoint, OPoint) 和 area(),分别用于设置矩形和获取其面积。
后来,我们需要将这个类移植到一个新的软件中,这个软件的接口使用的是IRect,使用左上角的两个坐标 (left, top) 和宽 (width)、高 (height)。同样有两个方法 setRect(int left, int top, int with, int height) 和 getArea()。那么我们如何处理?看下面的代码:
原始类和接口类:
类适配器:
对象适配器:
测试代码:
运行结果:
--------------Original Rectangle-------------
left= 1, top= 2, right= 4, bottom= 4
area= 6
-----------Class Adapter Rectangle-----------
left= 1, top= 2, width= 3, height= 2
area= 6
----------Object Adapter Rectangle-----------
left= 1, top= 2, width= 3, height= 2
area= 6
效果完全一致。适配器很成功。
在这里我想吐个槽,《Head First》是一本针对Java语言的书,在说到类适配器的时候只是一代而过,也没有示例代码,只说用多继承,又没说清楚怎么用,搞得我非常纠结。查阅网上的多继承资料,也没有突破,最后终于想到了四人帮的《Design Patterns》,他们是用c++为例做的介绍。于是我通过研究他们的示例代码才终于弄明白类适配器要如何使用。其关键在于:
共有继承客户接口,私有继承原始类!
在《Design Patterns》上的原话是这样: "The key to class adapters is to use one inheritance branch to inherit the interface and another branch to inherit the implementation. The usual way to make this distinction in C++ is to inherit the interface publicly and inherit the implementation privately."
这么一弄,豁然开朗。
共有继承客户接口保证了接口是客户所期望的。而私有继承原始类使得原始类的方法隐藏起来,不会被外界代码继续调用。真是奇妙的c++!
2. 外观模式
外观模式实际上实现方法完全与适配器相同。只是侧重点不一样。外观模式的定义如下:
提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。
什么意思呢?实际上很简单,就是将复杂的系统操作傻瓜化,提供一个傻瓜接口。
从本质上来说,外观模式难道不是接口转换吗?所以其实现方式是与适配器模式完全相同的。这里我就不再举例。
3. 总结
让我们回想一下,命令模式是不是也能具有外观模式这样的功能?将复杂的命令封装成统一接口,然后使用一个串将这一系列复杂的接口连接起来。一键操作。
乍看起来,似乎的确是这样,但是请注意命令模式的定义,命令模式只是需要保证一个统一的命令接口,以方便组织,而并未强调简化操作或接口。
所以,学习的设计模式多起来以后,会发现他们其实都大同小异,实现方式就那么几种,稍微有些变化就成了一个新的设计模式。
那为什么还要单独写出这么多设计模式呢?我想,针对不同的应用场合,你能根据这些设计模式的侧重点,很快的对应到一个合适的模式,并能很快的实现这个系统,简化开发流程,这就是设计模式的作用了。
打个不太恰当的比方,设计模式就像提供了一个表,你在使用复杂函数的时候,使用查找表的方法肯定比一步步计算要快得多。