适配器模式
适配器模式将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以无缝协作。在GOF95中,适配器被分类为结构型模式。适配器和装饰器具有共同的别名:Wrapper。
对象适配器:使用单重继承+组合,类图如下:
类适配器:使用多重继承,可能需要支持多继承的语言(例如C++、Python),类图如下:
- Client只能看到期望的目标接口,不知道接口居然是李鬼
- Adaptor需要实现目标接口,同时与Adaptee形成组合关系,转调Adaptee来实现目标接口中的方法
- Adaptor一般把操作都委托给Adaptee
- Adaptee的任何子类型都可以和Adaptor配合使用
- 对象适配器使用单重继承+组合来实现适配。类适配器则通过多重继承,Adaptor同时继承Target、Adaptee,并通过转调父类方法来进行适配。
其实生活中有大量适配器的例子。例如,香港的插座规格与大陆不一样,为了能在香港出差时正常使用笔记本电脑,你可能需要携带一个插座转换头,这个转换头就是一个适配器,笔记本电脑的变压器也是典型的例子,它把市电220V交流电转换为18.3V的直流电。
适配器模式与装饰模式在形式上很类似,都是包装一个对象。但适配器是改变接口,而装饰器则是添加功能。
适配器模式的优点:
- 类适配器可以在必要时覆盖Adaptee的方法,用以降低适配工作量
适配器模式的缺点:
- 如果被适配者的接口规模很大,那么适配器的规模也相应变大
- 类适配器模式缺乏灵活性,不能动态的改变Adaptee
适配器模式的适用时机:
- 想使用一个已经存在的类,但是它的接口不符合需求
- 向创建一个可复用的类,该类可以与其它不相关的、不可预见的(即接口不一定兼容)的类协同工作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
#include <iostream> using namespace std; /* 鸭子 */ class Duck { public: virtual void quack() { cout << "Quack quack, too hot, me die." << endl; } } /* 鸡 */ class Chicken { public: virtual void croak() { cout << "Crock crock, I'm not a duck, let me go!" << endl; } void shutup() { } }; /* 烤活鸭炉 */ class AliveDuckOven { public: void roast( Duck* duck ) { duck->quack(); //鸭子惨叫一声 } }; /* 粘上鸭毛的鸡 */ class FakeDuck : public Duck, public Chicken { public: virtual void quack() { Duck.quack(); } }; int main( int argc, char **argv ) { //老妖婆有一个烤炉 AliveDuckOven oven; //她每天都要村民进贡一只活鸭烤熟 Duck littleDuck; oven.roast( &littleDuck ); //村里的鸭子被吃光了,如果不给老妖婆进贡,她将每天杀死一个小孩 //村里有很多鸡,村民想了个好主意,把鸡粘上鸭毛 FakeDuck chickenWithDuckFeather = new FakeDuck(); //闭嘴,你这只笨鸡 chickenWithDuckFeather.shutup(); //像鸭子一样叫吧 chickenWithDuckFeather.quack(); //老妖婆就把这只粘上鸭毛的鸡烤着吃了 oven.roast( &chickenWithDuckFeather ); } |
JDBC即Java DataBase Connectivity,它让所有Java应用程序通过一致性的接口来访问数据库,屏蔽了不同厂商的差异性:
数据库厂商负责实现JDBC接口(即Target接口族),并对自己私有的数据库对象进行适配,这些数据库对象的形式是五花八门的。
微软的ODBC与JDBC的想法很类似,在没有JDBC实现的情况下,Java可以通过JDBC/ODBC桥来连接数据库,JDBC和ODBC的接口是不同的,因此JDBC/ODBC桥也是适配器模式的应用。
JDK I/O包中不但有很多装饰模式,也有不少适配器模式的实现,例如:
1 2 |
java.io.InputStreamReader(InputStream); java.io.OutputStreamWriter(OutputStream); |
可以将字节流接口转换为字符流接口。
Cygwin和Wine这两种工具都是在架构层次上提现出适配器模式的例子。Cygwin用于在Windows环境下编译、运行基于Linux API的应用程序;Wine则用于在Linux下不加修改的运行Windows应用程序。这两种工具都在底层代码上做了大量的适配工作。
- 适配多个Adaptee,某些情况下需要使用多个Adaptee才能实现Target中的功能
- 智能适配器:Adaptor实现一些Adaptee没有实现,但是Target中定义的功能
- 缺省适配:为接口提供一个默认的实现,有了它,就不用去直接实现接口,而继承此缺省适配类就可以了,可以减少代码量
- 双向适配器:同时实现Target、Adaptee规定的接口
Leave a Reply