什么是适配器模式?
适配器模式是一种结构型设计模式,用于解决接口不兼容的问题。通过适配器模式,我们可以将一个类的接口转换成客户端期望的接口,使得原本不能一起工作的类能够协同工作。简单来说,它的作用类似于电源适配器,将不兼容的插头接入到兼容的插座中。
适配器模式的应用场景
- 接口转换:当一个系统需要使用现有的类,但它的接口与系统的其他部分不兼容时,可以使用适配器模式。
- 集成遗留代码:当需要将旧系统集成到新系统中,而两者的接口不一致时,适配器模式是一种很好的解决方案。
- 接口统一:希望通过统一的接口来调用多种功能不同但相似的类。
适配器模式的组成
适配器模式的核心由以下三个部分组成:
- 目标接口(Target):定义客户端期望的接口标准。
- 适配者(Adaptee):现有的类,提供了功能但接口与目标接口不兼容。
- 适配器(Adapter):连接目标接口和适配者,负责接口的转换。
适配器模式的类图结构如下:
---------------------
| Client |
---------------------
|
v
---------------------
| Target (接口) |
---------------------
^
|
---------------------
| Adapter (适配器) |
---------------------
|
v
---------------------
| Adaptee (被适配者) |
---------------------
贴近实际的案例:媒体播放器适配器
问题背景:
假设我们需要开发一个媒体播放器,它需要支持多种类型的文件播放:
- 音频播放器支持
.mp3
文件。 - 视频播放器支持
.mp4
和.avi
文件。
由于音频播放器和视频播放器的接口不同,我们希望通过统一的接口 MediaPlayer
来调用这些播放器的功能。
案例代码实现
以下是完整的适配器模式实现代码,并对每个模块进行中文解读。
目标接口(Target)
// 定义统一的播放器接口
class MediaPlayer {
public:
virtual void play(const std::string& audioType, const std::string& fileName) = 0;
virtual ~MediaPlayer() = default;
};
解读:
MediaPlayer
是客户端期望的接口,定义了一个 play()
方法,所有播放器类都必须实现该方法。客户端通过此接口来播放不同类型的媒体文件。
适配者(Adaptee)
#include <iostream>
// 高级媒体播放器接口
class AdvancedMediaPlayer {
public:
virtual void playMp4(const std::string& fileName) = 0;
virtual void playAvi(const std::string& fileName) = 0;
virtual ~AdvancedMediaPlayer() = default;
};
// MP4 播放器
class Mp4Player : public AdvancedMediaPlayer {
public:
void playMp4(const std::string& fileName) override {
std::cout << "Playing mp4 file: " << fileName << std::endl;
}
void playAvi(const std::string& fileName) override {
// 不支持播放 AVI 文件
std::cout << "AVI format not supported by Mp4Player." << std::endl;
}
};
// AVI 播放器
class AviPlayer : public AdvancedMediaPlayer {
public:
void playMp4(const std::string& fileName) override {
// 不支持播放 MP4 文件
std::cout << "MP4 format not supported by AviPlayer." << std::endl;
}
void playAvi(const std::string& fileName) override {
std::cout << "Playing avi file: " << fileName << std::endl;
}
};
解读:
AdvancedMediaPlayer
是高级媒体播放器的接口,定义了两种高级格式(.mp4
和.avi
)的播放方法。Mp4Player
和AviPlayer
是具体的播放器实现类,分别实现了.mp4
和.avi
的播放功能。
适配器(Adapter)
// 媒体适配器,将 AdvancedMediaPlayer 适配为 MediaPlayer
class MediaAdapter : public MediaPlayer {
private:
AdvancedMediaPlayer* advancedMediaPlayer;
public:
MediaAdapter(const std::string& audioType) {
if (audioType == "mp4") {
advancedMediaPlayer = new Mp4Player();
} else if (audioType == "avi") {
advancedMediaPlayer = new AviPlayer();
} else {
advancedMediaPlayer = nullptr;
}
}
void play(const std::string& audioType, const std::string& fileName) override {
if (audioType == "mp4") {
advancedMediaPlayer->playMp4(fileName);
} else if (audioType == "avi") {
advancedMediaPlayer->playAvi(fileName);
} else {
std::cout << "Invalid media type: " << audioType << std::endl;
}
}
~MediaAdapter() {
delete advancedMediaPlayer;
}
};
解读:
MediaAdapter
适配器实现了MediaPlayer
接口。- 根据媒体类型的不同,适配器创建对应的高级播放器实例(如
Mp4Player
或AviPlayer
),并将play()
方法调用委托给高级播放器。
具体播放器类
// 默认播放器,支持 MP3,使用适配器处理高级格式
class AudioPlayer : public MediaPlayer {
public:
void play(const std::string& audioType, const std::string& fileName) override {
if (audioType == "mp3") {
std::cout << "Playing mp3 file: " << fileName << std::endl;
} else if (audioType == "mp4" || audioType == "avi") {
MediaAdapter adapter(audioType);
adapter.play(audioType, fileName);
} else {
std::cout << "Unsupported media type: " << audioType << std::endl;
}
}
};
解读:
AudioPlayer
是客户端直接使用的播放器类:
- 它可以直接播放
.mp3
文件。 - 对于
.mp4
和.avi
文件,它会通过MediaAdapter
适配器来处理。
客户端代码
int main() {
AudioPlayer player;
player.play("mp3", "song.mp3");
player.play("mp4", "video.mp4");
player.play("avi", "movie.avi");
player.play("flv", "clip.flv"); // 不支持的格式
return 0;
}
解读:
客户端使用 AudioPlayer
的 play()
方法播放不同类型的文件,完全不关心文件格式和具体的播放器实现。
运行结果
Playing mp3 file: song.mp3
Playing mp4 file: video.mp4
Playing avi file: movie.avi
Unsupported media type: flv
适配器模式优点
-
解耦:
客户端与具体实现解耦,只依赖目标接口,代码更易于维护。 -
复用性:
可以复用现有的类,而无需修改其代码。 -
扩展性:
如果需要支持新的格式,只需添加新的高级播放器类,并扩展适配器逻辑即可。
适配器模式缺点
-
额外复杂度:
适配器增加了一层间接调用,可能引入一定的复杂性。 -
性能影响:
由于多了一层接口转换,可能对性能造成微小的影响(通常可以忽略不计)。
总结
适配器模式是软件开发中非常重要的模式之一,尤其在需要集成遗留系统或引入第三方库时,可以显著简化开发。通过适配器模式,我们可以在接口不一致的情况下,让系统更灵活地扩展和集成功能,同时遵循面向对象设计原则(如开闭原则和依赖倒置原则)。
以下是整合后的完整代码
#include <iostream>
#include <string>
// ===========================
// 目标接口(Target):定义客户端需要的统一接口
// ===========================
class MediaPlayer {
public:
// 定义播放方法,接受文件类型和文件名作为参数
virtual void play(const std::string& audioType, const std::string& fileName) = 0;
virtual ~MediaPlayer() = default;
};
// ===========================
// 适配者接口(Adaptee):定义高级媒体播放器的接口
// ===========================
class AdvancedMediaPlayer {
public:
// 播放 MP4 文件
virtual void playMp4(const std::string& fileName) = 0;
// 播放 AVI 文件
virtual void playAvi(const std::string& fileName) = 0;
virtual ~AdvancedMediaPlayer() = default;
};
// ===========================
// 具体适配者(Concrete Adaptee):MP4 播放器
// ===========================
class Mp4Player : public AdvancedMediaPlayer {
public:
// 实现播放 MP4 文件的方法
void playMp4(const std::string& fileName) override {
std::cout << "Playing mp4 file: " << fileName << std::endl;
}
// 不支持播放 AVI 文件
void playAvi(const std::string& fileName) override {
std::cout << "AVI format not supported by Mp4Player." << std::endl;
}
};
// ===========================
// 具体适配者(Concrete Adaptee):AVI 播放器
// ===========================
class AviPlayer : public AdvancedMediaPlayer {
public:
// 不支持播放 MP4 文件
void playMp4(const std::string& fileName) override {
std::cout << "MP4 format not supported by AviPlayer." << std::endl;
}
// 实现播放 AVI 文件的方法
void playAvi(const std::string& fileName) override {
std::cout << "Playing avi file: " << fileName << std::endl;
}
};
// ===========================
// 适配器(Adapter):将高级播放器接口适配为目标接口
// ===========================
class MediaAdapter : public MediaPlayer {
private:
AdvancedMediaPlayer* advancedMediaPlayer; // 适配器持有高级播放器的引用
public:
// 根据媒体类型选择适配的高级播放器
MediaAdapter(const std::string& audioType) {
if (audioType == "mp4") {
advancedMediaPlayer = new Mp4Player();
} else if (audioType == "avi") {
advancedMediaPlayer = new AviPlayer();
} else {
advancedMediaPlayer = nullptr;
}
}
// 将目标接口的调用转换为高级播放器接口的调用
void play(const std::string& audioType, const std::string& fileName) override {
if (audioType == "mp4") {
advancedMediaPlayer->playMp4(fileName);
} else if (audioType == "avi") {
advancedMediaPlayer->playAvi(fileName);
} else {
std::cout << "Invalid media type: " << audioType << std::endl;
}
}
// 析构函数,释放高级播放器实例
~MediaAdapter() {
delete advancedMediaPlayer;
}
};
// ===========================
// 具体播放器类(Concrete Target):实现默认的播放器逻辑
// ===========================
class AudioPlayer : public MediaPlayer {
public:
// 实现统一的播放方法,支持 MP3,并通过适配器支持 MP4 和 AVI
void play(const std::string& audioType, const std::string& fileName) override {
if (audioType == "mp3") {
// 如果是 MP3 文件,直接播放
std::cout << "Playing mp3 file: " << fileName << std::endl;
} else if (audioType == "mp4" || audioType == "avi") {
// 对于 MP4 和 AVI 文件,使用适配器
MediaAdapter adapter(audioType);
adapter.play(audioType, fileName);
} else {
// 不支持的文件类型
std::cout << "Unsupported media type: " << audioType << std::endl;
}
}
};
// ===========================
// 客户端代码:测试播放器功能
// ===========================
int main() {
AudioPlayer player; // 创建播放器实例
// 测试播放 MP3 文件
player.play("mp3", "song.mp3");
// 测试播放 MP4 文件
player.play("mp4", "video.mp4");
// 测试播放 AVI 文件
player.play("avi", "movie.avi");
// 测试播放不支持的文件类型
player.play("flv", "clip.flv");
return 0;
}
运行结果
Playing mp3 file: song.mp3
Playing mp4 file: video.mp4
Playing avi file: movie.avi
Unsupported media type: flv