Adapter模式:
Adapter模式将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
系统的数据和行为都正确,但接口不符时,我们应该考虑使用适配器,目的是使控制范围之外的一个原有对象与某个借口匹配。适配器模式主要应用于希望复用一些现存的类,但是接口又与复用环境要求不一致的情况。通常在双方都不太容易修改的时候再使用适配器模式适配。
举个例子,假如公司设计一系统时考虑使用第三方组件,而这个组件的接口与我们自己的系统接口是不相同的,而我们也完全没有必要为了迎合它而改动自己的接口,这时,就可以考虑用适配器来解决接口不同的问题。
Adapt模式有两种实现办法,一种是采用继承原有接口类的方法,一种是采用组合原有接口类的方法。
用《大话设计模式》中的NBA球员作为例子,姚明初到NBA时,需要翻译人员(适配器)作为他与其他人沟通的桥梁。
Adapter模式典型的结构图为:
Adapter Pattern(类模式)结构图
Adapter Pattern(对象模式)结构图
下面的Adapter Pattern(类模式)的实现代码:
//Adapter.h
#ifndef _ADAPTER_H_
#define _ADAPTER_H_
// 球员
class Player
{
public:
Player();
virtual ~Player();
virtual void Attack();
protected:
private:
};
// 翻译官适配器
class TranslatorAdaptee
{
public:
TranslatorAdaptee();
~TranslatorAdaptee();
void SpecificAttack();
protected:
private:
};
// 外籍球员,如姚明
class ForeignPlayer : public Player, private TranslatorAdaptee
{
public:
ForeignPlayer();
~ForeignPlayer();
void Attack();
};
#endif //~_ADAPTER_H_
//Adapter.cpp
#include "Adapter.h"
#include <iostream>
Player::Player()
{
}
Player::~Player()
{
}
void Player::Attack()
{
std::cout<<"Player::Attack..."<<std::endl;
}
TranslatorAdaptee::TranslatorAdaptee()
{
}
TranslatorAdaptee::~TranslatorAdaptee()
{
}
void TranslatorAdaptee::SpecificAttack()
{
std::cout<<"TranslatorAdaptee::SpecificAttack...外籍球员进攻"<<std::endl;
}
ForeignPlayer::ForeignPlayer()
{
}
ForeignPlayer::~ForeignPlayer()
{
}
void ForeignPlayer::Attack()
{
this->SpecificAttack();
}
//main.cpp
#include "Adapter.h"
#include <iostream>
using namespace std;
int main(int argc,char* argv[])
{
// 普通球员进攻
Player* play = new Player;
play->Attack();
// 外籍球员(如姚明)进攻,由翻译适配器告诉外籍球员
Player* fplay = new ForeignPlayer();
fplay->Attack();
return 0;
}
下面的Adapter Pattern(对象模式)的实现代码:
//Adapter.h
#ifndef _ADAPTER_H_
#define _ADAPTER_H_
// 球员
class Player
{
public:
Player();
virtual ~Player();
virtual void Attack();
protected:
private:
};
// 翻译官适配器
class TranslatorAdaptee
{
public:
TranslatorAdaptee();
~TranslatorAdaptee();
void SpecificAttack();
protected:
private:
};
// 外籍球员,如姚明
class ForeignPlayer:public Player
{
public:
ForeignPlayer(TranslatorAdaptee* ade);
~ForeignPlayer();
void Attack();
protected:
private:
TranslatorAdaptee* _ade;
};
#endif //~_ADAPTER_H_
//Adapter.cpp
#include "Adapter.h"
#include <iostream>
Player::Player()
{
}
Player::~Player()
{
}
void Player::Attack()
{
std::cout<<"Player::Attack..."<<std::endl;
}
TranslatorAdaptee::TranslatorAdaptee()
{
}
TranslatorAdaptee::~TranslatorAdaptee()
{
}
void TranslatorAdaptee::SpecificAttack()
{
std::cout<<"TranslatorAdaptee::SpecificAttack...外籍球员进攻"<<std::endl;
}
ForeignPlayer::ForeignPlayer(TranslatorAdaptee* ade)
{
this->_ade = ade;
}
ForeignPlayer::~ForeignPlayer()
{
}
void ForeignPlayer::Attack()
{
_ade->SpecificAttack();
}
//main.cpp
#include "Adapter.h"
#include <iostream>
using namespace std;
int main(int argc,char* argv[])
{
// 普通球员进攻
Player* play = new Player;
play->Attack();
// 外籍球员(如姚明)进攻,由翻译适配器告诉外籍球员
TranslatorAdaptee* ade = new TranslatorAdaptee;
Player* fplay = new ForeignPlayer(ade);
fplay->Attack();
return 0;
}
在类模式Adapter中,我们通过private继承TranslatorAdaptee获得实现继承的效果,而通过public继承Player获得接口继承的效果。
在Adapter模式的两种模式中,有一个很重要的概念就是接口继承和实现继承的区别和联系。接口继承和实现继承是面向对象领域的两个重要的概念。
接口继承:指的是通过继承,子类获得了父类的接口;
实现继承:指的是通过继承,子类获得了父类的实现。
在C++中的public继承既是接口继承又是实现继承,因为子类在继承了父类后既可以对外提供父类中的接口操作,又可以获得父类的接口实现。当然我们可以通过一定的方式和技术模拟单独的接口继承和实现继承,例如我们可以通过private继承获得实现继承的效果(private继承后,父类中的接口都变为private,当然只能是实现继承了。),通过纯抽象基类模拟接口继承的效果,但是在C++中pure virtual function也可以提供默认实现,因此这是不纯正的接口继承,但是在Java中我们可以interface来获得真正的接口继承了。