适配器模式(Adapter Pattern):将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。
适配器模式中主要的类
Target(目标抽象类):目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。
Adapter(适配器类):适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,适配器类是适配器模式的核心,在对象适配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系。
Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。
对象适配器模式
结构图
案例
使用TCP进行网络通信,一般都会包含创建套接字、绑定套接字、监听套接字、连接套接字等4个过程。某网络公司已经有一套成熟的套接字软件包,里面包含网络通信的4个过程。现欲开发一套新的聊天软件,决定复用已经成熟的套接字软件包。
SocketPackage就是成熟的套接字软件包,也就是Adaptee适配者类,包含创建套接字、绑定套接字、监听套接字、连接套接字等成熟方法; Socket是目标抽象类Target,定义了客户希望的方法; SocketAdapter为适配器类,将成熟软件包里面的方法转为Socket类中客户希望的方法。
#include <iostream> #include <string> using namespace std; //套接字包 适配者 class SocketPackage { public: void CreateSpecificSocket() { cout << "创建套接字" << endl; } void BindSpecificSocket() { cout << "绑定套接字" << endl; } void ListenSpecificSocket() { cout << "监听套接字" << endl; } void ConnecSpecifictSocket() { cout << "连接套接字" << endl; } }; //抽象套接字类 class Socket { public: //创建套接字 virtual void CreateSocket() = 0; //绑定套接字 virtual void BindSocket() = 0; //监听套接字 virtual void ListenSocket() = 0; //连接套接字 virtual void ConnectSocket() = 0; }; //套接字适配器 class SocketAdapter : public Socket { private: SocketPackage * m_pSocketPackage; public: //构造函数,创建一个需要复用的套接字包对象 SocketAdapter() { m_pSocketPackage = new SocketPackage(); } //销毁需要复用的套接字包对象 ~SocketAdapter() { if( NULL != m_pSocketPackage ) { delete m_pSocketPackage; m_pSocketPackage = NULL; } } //创建套接字 void CreateSocket() { m_pSocketPackage->CreateSpecificSocket(); } //绑定套接字 void BindSocket() { m_pSocketPackage->BindSpecificSocket(); } //监听套接字 void ListenSocket() { m_pSocketPackage->ListenSpecificSocket(); } //连接套接字 void ConnectSocket() { m_pSocketPackage->ConnecSpecifictSocket(); } }; int main() { //创建套接字适配器对象 Socket * pSocketAdapter = new SocketAdapter(); //使用适配器进行套接字操作 pSocketAdapter->CreateSocket(); pSocketAdapter->BindSocket(); pSocketAdapter->ListenSocket(); pSocketAdapter->ConnectSocket(); //销毁操作 delete pSocketAdapter; pSocketAdapter = NULL; return 0; }
类适配器模式
除了对象适配器模式之外,适配器模式还有一种形式,那就是类适配器模式,类适配器模式和对象适配器模式最大的区别在于适配器和适配者之间的关系不同,对象适配器模式中适配器和适配者之间是关联关系,而类适配器模式中适配器和适配者是继承关系。不管是对象适配器还是类适配器,都是为了复用适配者类的功能。
结构图
#include <iostream> #include <string> using namespace std; //套接字包 适配者 class SocketPackage { public: void CreateSpecificSocket() { cout << "创建套接字" << endl; } void BindSpecificSocket() { cout << "绑定套接字" << endl; } void ListenSpecificSocket() { cout << "监听套接字" << endl; } void ConnecSpecifictSocket() { cout << "连接套接字" << endl; } }; //抽象套接字类 class Socket { public: //创建套接字 virtual void CreateSocket() = 0; //绑定套接字 virtual void BindSocket() = 0; //监听套接字 virtual void ListenSocket() = 0; //连接套接字 virtual void ConnectSocket() = 0; }; //套接字适配器 class SocketAdapter : public Socket, SocketPackage { public: //创建套接字 void CreateSocket() { CreateSpecificSocket(); } //绑定套接字 void BindSocket() { BindSpecificSocket(); } //监听套接字 void ListenSocket() { ListenSpecificSocket(); } //连接套接字 void ConnectSocket() { ConnecSpecifictSocket(); } }; int main() { //创建套接字适配器对象 Socket * pSocketAdapter = new SocketAdapter(); //使用适配器进行套接字操作 pSocketAdapter->CreateSocket(); pSocketAdapter->BindSocket(); pSocketAdapter->ListenSocket(); pSocketAdapter->ConnectSocket(); //销毁操作 delete pSocketAdapter; pSocketAdapter = NULL; return 0; }
案例习题
for_each遍历vector容器时,函数对象只允许有一个参数。
class myclass{ public: void operator()(int i){ cout << i << " " ; } }; for_each(v1.begin(),v1.end(),myclass());
现在需要实现一个适配器每次给数组容器的每一个成员都加上一个自己设定的数M然后输出。
代码实现:对象适配器模式
#include <iostream> #include<vector> #include<algorithm> using namespace std; class MyPrint{ //适配者 //成员变量 public: void operator()(int v1, int v2){//对()进行重载 cout << v1 + v2 << endl; } }; //定义目标接口 class Target{ //成员变量 public: virtual void operator()(int v) = 0; }; //适配器 class Adapter :public Target{ public: MyPrint print; int param; public: Adapter(int param){ this->param = param; } public: virtual void operator()(int v){ print(v, param); } }; int main() { vector<int> v; for (int i = 0; i < 10; i++) { v.push_back(i); } Adapter A(10); for_each(v.begin(), v.end(), A); return 0; }