2018-09-19 22:12:25
适配器模式
适配器模式(Adapter):将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。(注:C++ std的bind函数不就是做个的吗)。适配器模式,就像是一个转接头,我只有三项插座,但是这个电器只有两个插头,那么至于要在中间加一个转接器,让三项插座看起来是双项插座,这样就能使用了。它的作用就是“有个东西,已经在这里了,你要使用它,但是又不能使用,短时间内又不能改变它,那么这个时候你就要想办法去适配它”。所谓适配器就是使一个东西适合另一个东西的东西。
何时使用适配器
在软件开发中,系统的数据和行为都正确,但接口不符合时,应该考虑适配器,目的是使控制范围之外的一个原有对象与某个接口匹配。适配器模式主要适用于希望复用一些现存的类,但是接口又和复用环境不一致的环境。适配器模式分两种:类适配器模式和对象适配器模式。类适配器模式通过多重继承对一个接口与另一个接口进行匹配。所以适配器模式的使用场景就是两个类所做的事情相同或者相似,但是具有不同的接口是可以使用它。这样一来,客户端代码就可以同一调用一个接口了。在双方都不太容易修改接口的时候再使用适配器模式。
适配器模式UML图
类适配器(UML):适配器核心Adapter直接继承自Adaptee
对象适配器(UML):(核心Adapter和Adaptee是关联关系)
Target:这是客户端所期待的接口,目标可以是具体的或者抽象的类,也可以是接口(注:C++没有接口的概念,但虚基类可以替代接口的作用)
Adapter:通过内部包装一个Adaptee对象,把源接口转换成目标接口。是适配器模式的核心,具有将Adaptee包装成Target的职责。
Aadptee:需要适配的类
适配器的优缺点
本部分来自于博客:https://blog.csdn.net/liang19890820/article/details/66973296
类适配器包含以下特点:
- 由于 Adapter 直接继承自 Adaptee 类,所以,在 Adapter 类中可以对 Adaptee 类的方法进行重定义。
- 如果在 Adaptee 中添加了一个抽象方法,那么 Adapter 也要进行相应的改动,这样就带来高耦合。
- 如果 Adaptee 还有其它子类,而在 Adapter 中想调用 Adaptee 其它子类的方法时,使用类适配器是无法做到的。
对象适配器包含以下特点:
- 有的时候,你会发现,去构造一个 Adaptee 类型的对象不是很容易。
- 当 Adaptee 中添加新的抽象方法时,Adapter 类不需要做任何调整,也能正确的进行动作。
- 可以使用多态的方式在 Adapter 类中调用 Adaptee 类子类的方法。
由于对象适配器的耦合度比较低,所以在很多的书中都建议使用对象适配器。
优点:
- 可以让任何两个没有关联的类一起运行
- 提高了类的复用
- 增加了类的透明度
- 灵活性好
缺点:
- 过多地使用适配器,会让系统非常零乱,不利于整体把控。
例如,看到调用的是 A 接口,内部却被适配成了 B 接口的实现,系统如果出现太多类似情况,无异于一场灾难。因此,如果不是很必要,可以不使用适配器,而是直接对系统进行重构。
适用场景
- 当想使用一个已存在的类,而它的接口不符合需求时。
- 你想创建一个可复用的类,该类可以与其他不相关的类或不可预见的类协同工作。
- 你想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口,对象适配器可以适配它的父接口。
代码示例
博客上讲的例子三项插头和双项插头的例子很能恰如其分的说明适配器模式,但是我选择实现《大话设计模式》一书中的本土球员和外籍球员这个案例。背景:教练和一些球员都是英语使用者,他们不懂中文,有一些中国籍球员,他们不懂英文,这个时候教练要正确的把战术传达给中国籍球员,中间就需要一个翻译,翻译在适配器设计模式中就是最重要的Adapter,它负责把英文转换为中文,并传达给中国籍球员。
1.要被转换到的目标接口(可以是个虚基类):Target(UML中的Target),在本例中是Player。(考虑一下是外籍球员要适配本土球员,所以实际上客户端调用的接口应该是Player提供的方法,客户代码相当于是教练,教练把战术告诉翻译,翻译再传给外籍球员,这就完成了一次适配过程,外籍球员要适配本土球员,入乡随俗嘛,那么这个翻译就相当于是适配器) Player就是UML中的Target。
#ifndef PLAYER_H_ #define PLAYER_H_ #include <string> class Player { public: virtual void attackInEnglish() = 0; virtual void defenseInEnglish() = 0; Player() = default; virtual ~Player() = default; protected: std::string m_strName; }; #endif
#ifndef LOCALPLAYER_H_ #define LOCALPLAYER_H_ #include <iostream> #include "Player.h" class LocalPlayer:public Player { public: void attackInEnglish() override; void defenseInEnglish() override; ~LocalPlayer() = default; LocalPlayer(const std::string strName) { m_strName = strName; }; }; #endif #include "LocalPlayer.h" void LocalPlayer::attackInEnglish() { std::cout << m_strName << " Attack!" << std::endl; } void LocalPlayer::defenseInEnglish() { std::cout << m_strName << " Defense!" << std::endl; }
2.外籍球员(需要进行适配的类)
#ifndef FOREIGNPLAYER_H_ #define FOREIGNPLAYER_H_ #include <iostream> #include <string> class ForeignPlayer { public: void attackInChinese(); void defenseInChinese(); ForeignPlayer(const std::string strName):m_strName(strName){}; ForeignPlayer() = default; void setName(const std::string strName) { m_strName = strName; } ~ForeignPlayer() = default; private: std::string m_strName; }; #endif #include "ForeignPlayer.h" void ForeignPlayer::attackInChinese() { std::cout << m_strName << " Jin Gong!" << std::endl; } void ForeignPlayer::defenseInChinese() { std::cout << m_strName << " Fang Shou!" << std::endl; }
3.翻译(适配器,UML图中的Adapter)
#ifndef ADAPTER_H_ #define ADAPTER_H_ #include "Player.h" #include "ForeignPlayer.h" class Adapter : public Player { public: void attackInEnglish() override; void defenseInEnglish() override; Adapter(const std::string strName) { m_strName = strName; } ~Adapter() = default; private: ForeignPlayer m_objForeignPlayer; }; #endif #include "Adapter.h" void Adapter::attackInEnglish() { m_objForeignPlayer.setName(m_strName); m_objForeignPlayer.attackInChinese(); } void Adapter::defenseInEnglish() { m_objForeignPlayer.setName(m_strName); m_objForeignPlayer.defenseInChinese(); }
4.教练(客户端代码,UML图中的client)
#include "LocalPlayer.h" #include "Adapter.h" int main(int argc,char *argv[]) { LocalPlayer objLocalPlayer("William"); objLocalPlayer.attackInEnglish(); LocalPlayer objLocalPlayer2("Green"); objLocalPlayer2.defenseInEnglish(); Adapter objForeignPlayer("YaoMing"); objForeignPlayer.attackInEnglish(); return (1); }