适配器模式
1 概念
将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器。适配器模式将两个不兼容的类接口"适配"成兼容的类接口,让其能在一起组合工作,适配器模式的核心做法是将类自身的接口封装在一个已经存在的类方法中。
2 意图(Intent)
将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能在一起工作的那些类可以一起工作。
3 动机(Motivate)
在软件系统中,由于应用环境的变化, 常常需要将"一些现存的对象"放在新的环境中应用,但是新环境要求的接口是这些现存对象所不满足的。如何应对这种"迁移的变化"?如何既能利用现有对象的良好实现,同时又能满足新的应用环境所要求的接口?
4 类图结构

5 角色定义
-
Target(目标抽象类):目标抽象类定义客户要用的特定领域的接口,可以是个抽象类或接口,也可以是具体类;
-
Adapter(适配器类):适配器可以调用调用另一个接口,作为一个转换器,对Adaptee和Target进行适配。在对象适配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系。
-
Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配。适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下甚至没有适配者类的源代码。
-
Client(客户类):在客户类中针对目标抽象类进行编程,调用在目标抽象类中定义的业务方法。
6 程序示例
机器人适配例子
示例类图

适配者Dog类
cpp
/**
* @brief 适配者Dog类
*/
class Dog
{
public:
Dog() = default;
~Dog() = default;
public:
void Wang();
void Run();
};
void Dog::Wang()
{
qDebug() << "狗汪汪叫!";
}
void Dog::Run()
{
qDebug() << "狗快快跑!";
}
适配者Bird类
cpp
/**
* @brief 适配者Bird类
*/
class Bird
{
public:
Bird() = default;
~Bird() = default;
public:
void Tweedle();
void Fly();
};
void Bird::Tweedle()
{
qDebug() << "鸟儿叽叽叫!";
}
void Bird::Fly()
{
qDebug() << "鸟儿高高飞!";
}
目标抽象机器人接口类
cpp
/**
* @brief 目标抽象机器人接口类
*/
class IRobot
{
public:
IRobot() = default;
virtual ~IRobot() = default;
public:
virtual void Cry() = 0;
virtual void Move() = 0;
};
适配器适配Dog类
cpp
/**
* @brief 适配器适配Dog类
*/
class DogAdapter: public IRobot
{
public:
DogAdapter();
~DogAdapter() = default;
public:
void Cry() override;
void Move() override;
private:
Dog* m_pDog;
};
DogAdapter::DogAdapter(): m_pDog(new Dog())
{
//
}
void DogAdapter::Cry()
{
qDebug() << "机器人模仿:";
m_pDog->Wang();
}
void DogAdapter::Move()
{
qDebug() << "机器人模仿:";
m_pDog->Run();
}
适配器适配Dog类
cpp
/**
* @brief 适配器适配Dog类
*/
class BirdAdapter: public IRobot
{
public:
BirdAdapter();
~BirdAdapter() = default;
public:
void Cry() override;
void Move() override;
private:
Bird* m_pBird;
};
BirdAdapter::BirdAdapter(): m_pBird(new Bird())
{
//
}
void BirdAdapter::Cry()
{
qDebug() << "机器人模仿:";
m_pBird->Tweedle();
}
void BirdAdapter::Move()
{
qDebug() << "机器人模仿:";
m_pBird->Fly();
}
测试函数
cpp
void ClientTest()
{
IRobot* pDogAdapter = new DogAdapter();
IRobot* pBirdAdapter = new BirdAdapter();
pDogAdapter->Cry();
pDogAdapter->Move();
pBirdAdapter->Cry();
pBirdAdapter->Move();
delete pDogAdapter;
delete pBirdAdapter;
}
三相插座适配例子
示例类图

三相插孔接口 TriplePin
cpp
/**
* @brief 三相插孔接口 TriplePin
*/
class TriplePin
{
public:
TriplePin() = default;
virtual ~TriplePin() = default;
public:
virtual void Electrify(int nLive, int nNull, int nEarth) = 0; // 火线,零线,地线
};
适配器类Adapter
cpp
/**
* @brief 适配器类Adapter
*/
class Adapter: public TriplePin
{
public:
Adapter(DualPin* pDualPinDevice);
~Adapter() = default;
public:
void Electrify(int nLive, int nNull, int nEarth) override;
private:
DualPin* m_pDualPinDevice;
};
Adapter::Adapter(DualPin* pDualPinDevice): m_pDualPinDevice(pDualPinDevice)
{
}
void Adapter::Electrify(int nLive, int nNull, int nEarth)
{
m_pDualPinDevice->Electrify(nLive, nNull);
}
两相插孔接口 DualPin
cpp
/**
* @brief 两相插孔接口 DualPin
*/
class DualPin
{
public:
DualPin() = default;
virtual ~DualPin() = default;
public:
virtual void Electrify(int nLive, int nNull) = 0; // 火线,零线
};
电视机类TV
cpp
/**
* @brief 电视机类TV
*/
class TV: public DualPin
{
public:
TV() = default;
virtual ~TV() = default;
public:
void Electrify(int nLive, int nNull) override;
};
void TV::Electrify(int nLive, int nNull)
{
qDebug() << "火线通电:" << nLive << ",零线通电:" << nNull;
qDebug() << "电视机开机!";
}
测试函数
cpp
/**
* @brief 测试函数
*/
void ClientTest()
{
DualPin* pDualPinDevice = new TV();
TriplePin* pTriplePinDevice = new Adapter(pDualPinDevice);
pTriplePinDevice->Electrify(1, 0, -1);
delete pDualPinDevice;
delete pTriplePinDevice;
}
7 思考小结
适配器模式优点:
- 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,而无须修改原有代码,这样就遵循了开闭原则。
- 增加了类的透明性和复用性,将具体的实现封装在适配者类中,对于客户端来说是透明的,而且提高了适配者的复用性。
- 灵活性和扩展性都非常好,通过使用配置文件,可以方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,也遵循了开闭原则。
适用适配器模式的场景:
- 系统需要使用现有的类,而这些类的接口不符合系统的需要。
- 想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括在一些可能在将来引进的类一起工作。