定义:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能在一起工作的那些类可以一起工作。
客户端需要一个target(目标)接口,但是不能直接重用已经存在的adaptee(适配者)类,因为它的接口和target接口不一致,所以需要adapter(适配器)将adaptee转换为target接口。前提是target接口和已存在的适配者adaptee类所做的事情是相同或相似,只是接口不同且都不易修改。如果在设计之初,最好避开这种模式,提高系统的可复用性。凡事都有例外,就是设计新系统的时候考虑使用第三方组件,因为我们就没必要为了迎合它修改自己的设计风格,可以尝试使用适配器模式。
在适配器模式中,我们通过增加一个新的适配器类来解决接口不兼容的问题,使得原本没有任何关系的类可以协同工作。
根据适配器类与适配者类的关系不同,适配器模式可分为对象适配器和类适配器两种,在对象适配器模式中,适配器与适配者之间是组合关系;在类适配器模式中,适配器与适配者之间是继承(或实现)关系。
记得在智能手机没有流行的时候,我要想用手机听很多歌曲,得用SD内存卡插到电脑上进行下载,然而SD卡的样子与电脑主机的接口并不适配,这时候就需要读卡器来完成这个工作,SD卡插入读卡器中接口,在把读卡器插到主机上,就可以下载想听的歌曲了读卡器就是适配器,SD卡就是目标,主机上的插口就是待适配的。
1.类适配器模式(adapter pattern)结构图:
实现代码:
#include <iostream>
using namespace std;
class SD
{
public:
virtual void Request()=0;
};
class PC{
public:
void download(){
cout<<"下载歌曲....."<<endl;
}
};
class Adapter :public SD,public PC
{
public:
virtual void Request()
{
cout<<"把SD卡插入读卡器,读卡器进行适配"<<endl;
download();
cout<<"下载完成"<<endl;
}
};
int main()
{
Adapter p;
p.Request(); // RealSubject
return 0;
}
运行结果:
优点:适配器可以重定义Adaptee的部分行为,相当于子类覆盖父类的部分实现方法。仅仅引入了一个对象,并不需要额外的引用来间接得到Adaptee
缺点:由于适配器直接继承了Adaptee,使得适配器不能和Adaptee的子类一起工作,因为继承是静态的关系,当适配器继承了Adaptee后,就不可能再去处理 Adaptee的子类了
- 对象适配器模式
代码实现:
#include <iostream>
using namespace std;
class SD
{
public:
virtual void Request()=0;
};
class PC{
public:
void download(){
cout<<"下载歌曲....."<<endl;
}
};
class Adapter :public SD
{
public:
PC a;
Adapter(PC b){
a=b;
}
virtual void Request()
{
cout<<"把SD卡插入读卡器,读卡器进行适配"<<endl;
a.download();
cout<<"下载完成"<<endl;
}
};
int main()
{
PC b;
Adapter p(b);
p.Request(); // RealSubject
return 0;
}
优点:目标类和适配者类解耦,增加了类的透明性和复用性,同时系统的灵活性和扩展性都非常好,更换适配器或者增加新的适配器都非常方便,符合"开闭原则"
缺点:过多的使用适配器,会让系统非常零乱,不易整体进行把握。