C++设计模式之适配器模式(一)

时间:2020-12-31 22:00:40
    现在的笔记本都只存在USB接口,用来口,电脑上存在的是USB接口,两者如何通信呢?可以使用USB转串口线连接电脑和嵌入式设备,其中USB一端连接电脑、串口和外部设备进行通信。然而在一些嵌入式设备上(例如ARM9),通常使用串口和电脑进行通信。嵌入式设备上存在的是串一端连接嵌入式设备。本来电脑和嵌入式设备由于接口不兼容,无法进行通信,而使用USB转串口线这个适配器,两者之间就可以正常进行数据通信。在设计模式中,也存在一种类似的模式,存在两个接口不同的类,可以使用一个适配器类来将一个接口转换为客户希望的另一个接口,称为适配器模式。 C++设计模式之适配器模式(一) 1、适配器模式

    在适配器模式中引入了一个被称为适配器(Adapter)的包装类,而它所包装的对象称为适配者(Adaptee),即被适配的类。适配器的实现就是把客户类的请求转化为对适配者的相应接口的调用。也就是说:当客户类调用适配器的方法时,在适配器类的内部将调用适配者类的方法,而这个过程对客户类是透明的,客户类并不直接访问适配者类。因此,适配器让那些由于接口不兼容而不能交互的类可以一起工作。

    适配器模式可以将一个类的接口和另一个类的接口匹配起来,而无须修改原来的适配者接口和抽象目标类接口。适配器模式定义如下:

适配器模式(Adapter Pattern):将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。

    在适配器模式中,我们通过增加一个新的适配器类来解决接口不兼容的问题,使得原本没有任何关系的类可以协同工作。根据适配器类与适配者类的关系不同,适配器模式可分为对象适配器和类适配器两种,在对象适配器模式中,适配器与适配者之间是关联关系;在类适配器模式中,适配器与适配者之间是继承(或实现)关系。在实际开发中,对象适配器的使用频率更高。

C++设计模式之适配器模式(一)

              对象适配器模式结构图

    在对象适配器模式结构图中包含如下几个角色:

    Target(目标抽象类):目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。

    Adapter(适配器类):适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,适配器类是适配器模式的核心,在对象适配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系。

    Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。

    根据对象适配器模式结构图,在对象适配器中,客户端需要调用request()方法,而适配者类Adaptee没有该方法,但是它所提供的specificRequest()方法却是客户端所需要的。为了使客户端能够使用适配者类,需要提供一个包装类Adapter,即适配器类。这个包装类包装了一个适配者的实例,从而将客户端与适配者衔接起来,在适配器的request()方法中调用适配者的specificRequest()方法。因为适配器类与适配者类是关联关系(也可称之为委派关系),所以这种适配器模式称为对象适配器模式。


2、Socket网络通信的设计与实现------对象适配器

    使用TCP进行网络通信,一般都会包含创建套接字、绑定套接字、监听套接字、连接套接字等4个过程。某网络公司已经有一套成熟的套接字软件包,里面包含网络通信的4个过程。现欲开发一套新的聊天软件,决定复用已经成熟的套接字软件包。
C++设计模式之适配器模式(一)      SocketPackage就是成熟的套接字软件包,也就是Adaptee适配者类,包含创建套接字、绑定套接字、监听套接字、连接套接字等成熟方法; Socket是目标抽象类Target,定义了客户希望的方法; SocketAdapter为适配器类,将成熟软件包里面的方法转为Socket类中客户希望的方法。     成熟的套接字软件包实现代码如下:
#ifndef _SOCKET_PACKAGE_H_
#define _SOCKET_PACKAGE_H_

#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;
}

};

#endif
    现欲开发的聊天软件定义了一个抽象类Socket,也就是目标抽象类Target,里面包含创建套接字、绑定套接字、监听套接字、连接套接字等客户希望的方法,但这些方法和成熟软件包中的方法不同。如:Socket类创建套接字方法为CreateSocket,而SocketPackage类创建套接字方法为CreateSpecificSocket。这两个方法不同,无法进行通信,为了复用成熟套接字软件包里面的方法,可以提供一个适配器类,继承于抽象的Socket类。在适配器中的方法将调用套接字软件包里面的方法。
    套接字适配器类实现代码如下:
#ifndef _SOCKET_H_
#define _SOCKET_H_

#include "SocketPackage.h"

//抽象套接字类
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();
}
};


#endif
    测试代码实现如下:
#include <iostream>
#include "Socket.h"

using namespace std;

int main()
{
//创建套接字适配器对象
Socket * pSocketAdapter = new SocketAdapter();

//使用适配器进行套接字操作
pSocketAdapter->CreateSocket();
pSocketAdapter->BindSocket();
pSocketAdapter->ListenSocket();
pSocketAdapter->ConnectSocket();

//销毁操作
delete pSocketAdapter;
pSocketAdapter = NULL;

return 0;
}
    编译并执行,结果如下:
C++设计模式之适配器模式(一)
    SocketAdapter套接字适配器类和SocketPackage套接字包是一种组合的关系,当调用套接字适配器类中的相应方法时,将调用套接字包对应的方法,通过组合的方式,实现对套接字软件包模块的复用。客户端不需要直接操作这个已经成熟的套接字软件包,而是由套接字适配器类进行委托操作,降低了客户端和适配者类的耦合。
    对象适配器模式中,适配器类Adapter和适配者类Adatpee类是一种关联关系,或者组合关系。适配器类维护一个适配者类的引用,在适配器的方法中调用相应的适配者类中的方法,实现对适配者功能的复用,这个过程对客户端是透明的,客户类不直接访问适配者类。