适配器模式
适配器模式是将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。好比日本现在就只提供110V的电压,而我的电脑就需要220V的电压,那怎么办啦?适配器就是干这活的,在不兼容的东西之间搭建一座桥梁,让二者能很好的兼容在一起工作。
在软件开发中,有的时候系统的数据和行为都正确,但接口不符合,我们应该考虑使用适配器模式,目的是使控制范围之外的一个原有对象与某个接口匹配。举个例子:在开发一个模块的时候,有一个功能点实现起来比较费劲,但是,之前有一个项目的模块实现了一样的功能点;但是现在这个模块的接口和之前的那个模块的接口是不一致的。此时,作为项目经理的你,该怎么办啦?使用适配器模式,将之前实现的功能点适配进新的项目了。
适配器模式实现步骤
- 适配器实现与其中一个现有对象兼容的接口
- 现有对象可以使用该接口安全地调用适配器方法
- 适配器方法被调用后将以另一个对象兼容的格式和顺序将请求传递给该对象
参考:适配器模式 --菜鸟教程
我们有一个 MediaPlayer 接口和一个实现了 MediaPlayer 接口的实体类 AudioPlayer。默认情况下,AudioPlayer 可以播放 mp3 格式的音频文件。
我们还有另一个接口 AdvancedMediaPlayer 和实现了 AdvancedMediaPlayer 接口的实体类。该类可以播放 vlc 和 mp4 格式的文件。
我们想要让 AudioPlayer 播放其他格式的音频文件。为了实现这个功能,我们需要创建一个实现了 MediaPlayer 接口的适配器类 MediaAdapter,并使用 AdvancedMediaPlayer 对象来播放所需的格式。
AudioPlayer 使用适配器类 MediaAdapter 传递所需的音频类型,不需要知道能播放所需格式音频的实际类。AdapterPatternDemo 类使用 AudioPlayer 类来播放各种格式。
使用C++实现:
//音乐播放器
class MediaPlayer
{
public:
virtual void play(string type,string name) = 0;
virtual ~MediaPlayer() {};
};
//高级音乐播放类
class AdvancedMediaPlayer
{
public:
virtual ~AdvancedMediaPlayer() {}
virtual void playVlc(string name) = 0;
virtual void playMp4(string name) = 0;
};
//支持Vlc的音乐播放器
class VlcPlayer :public AdvancedMediaPlayer
{
public:
~VlcPlayer() {}
void playVlc(string name)override
{
cout << "播放vlc类型音乐: " << name << endl;
}
void playMp4(string name)override {}//无需实现
};
//支持Mp4的音乐播放器
class Mp4Player :public AdvancedMediaPlayer
{
public:
~Mp4Player() {}
void playVlc(string name)override {}
void playMp4(string name)override
{
cout << "播放Mp4类型音乐: " << name << endl;
}
};
//接口适配器类
class MediaPlayerAdapter:public MediaPlayer
{
public:
MediaPlayerAdapter(string type)
{
if (type == "vlc")
{
if (pAdvance)
{
delete pAdvance;
}
pAdvance = new VlcPlayer;
}
else if (type == "mp4")
{
if (pAdvance)
{
delete pAdvance;
}
pAdvance = new Mp4Player;
}
}
~MediaPlayerAdapter() { if (pAdvance!=nullptr) delete pAdvance; }
void play(string type, string name)override
{
if (type == "vlc") pAdvance->playVlc(name);
else if (type == "mp4") pAdvance->playMp4(name);
}
private:
AdvancedMediaPlayer* pAdvance = nullptr;
};
//整合音乐播放器
class AutioPlayer :public MediaPlayer
{
public:
~AutioPlayer() { if (Mediaplayer != nullptr) delete Mediaplayer; }
void play(string type, string name)override
{
if (type == "mp3")
{
cout << "播放Mp3类型音乐: " << name << endl; //内置支持mp3类型音乐
}
else if(type == "vlc" || type == "mp4")
{
if (Mediaplayer)
{
delete Mediaplayer;
}
Mediaplayer = new MediaPlayerAdapter{ type };//转换适配音乐类型
Mediaplayer->play(type,name); //播放音乐
}
else
{
cout << "不支持的音乐类型: " << type << endl;
}
}
private:
MediaPlayerAdapter* Mediaplayer;
};
int main()
{
AutioPlayer* music = new AutioPlayer;
music->play("mp3", "青花瓷 -周杰伦");
music->play("mp4", "逆战 -张杰");
music->play("vlc", "一人我饮酒醉");
music->play("avi", "yellow");
delete music;
return 0;
}
优点
- 降低了去实现一个功能点的难度,可以对现有的类进行包装,就可以进行使用了
- 提高了项目质量,现有的类一般都是经过测试的,使用了适配器模式之后,不需要对旧的类进行全面的覆盖测试;
- 总的来说,提高了效率,降低了成本。
缺点
- 类适配器模式,由于多继承,可能会出现二义性
- 对象适配器模式,如果过多使用适配器模式,会导致代码阅读难度增大