适配器模式(Adapter Pattern)是一种结构型设计模式,它允许接口不兼容的类能够一起工作。适配器模式通过将一个类的接口转换成客户希望的另外一个接口,解决了接口不兼容的问题。
适配器模式三种实现方式
1. 抽象接口方式(类适配器)
这种方式通过继承被适配类并实现目标接口来实现适配。
C++实现示例:
#include <iostream>
#include <string>
// 目标接口
class MediaPlayer {
public:
virtual ~MediaPlayer() = default;
virtual void play(const std::string& audioType, const std::string& fileName) = 0;
};
// 被适配的接口
class AdvancedMediaPlayer {
public:
virtual ~AdvancedMediaPlayer() = default;
virtual void playVlc(const std::string& fileName) = 0;
virtual void playMp4(const std::string& fileName) = 0;
};
// 被适配的具体类
class VlcPlayer : public AdvancedMediaPlayer {
public:
void playVlc(const std::string& fileName) override {
std::cout << "Playing vlc file: " << fileName << std::endl;
}
void playMp4(const std::string& /*fileName*/) override {}
};
class Mp4Player : public AdvancedMediaPlayer {
public:
void playVlc(const std::string& /*fileName*/) override {}
void playMp4(const std::string& fileName) override {
std::cout << "Playing mp4 file: " << fileName << std::endl;
}
};
// 适配器
class MediaAdapter : public MediaPlayer {
private:
AdvancedMediaPlayer* advancedMusicPlayer;
public:
MediaAdapter(const std::string& audioType) {
if (audioType == "vlc") {
advancedMusicPlayer = new VlcPlayer();
} else if (audioType == "mp4") {
advancedMusicPlayer = new Mp4Player();
} else {
advancedMusicPlayer = nullptr;
}
}
~MediaAdapter() {
delete advancedMusicPlayer;
}
void play(const std::string& audioType, const std::string& fileName) override {
if (audioType == "vlc") {
advancedMusicPlayer->playVlc(fileName);
} else if (audioType == "mp4") {
advancedMusicPlayer->playMp4(fileName);
}
}
};
// 客户端
class AudioPlayer : public MediaPlayer {
private:
MediaAdapter* mediaAdapter;
public:
~AudioPlayer() {
delete mediaAdapter;
}
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 == "vlc" || audioType == "mp4") {
mediaAdapter = new MediaAdapter(audioType);
mediaAdapter->play(audioType, fileName);
} else {
std::cout << "Invalid media type: " << audioType << std::endl;
}
}
};
// 使用示例
int main() {
AudioPlayer audioPlayer;
audioPlayer.play("mp3", "song.mp3");
audioPlayer.play("vlc", "movie.vlc");
audioPlayer.play("mp4", "video.mp4");
audioPlayer.play("avi", "movie.avi");
return 0;
}
2. 代理对象方式(对象适配器)
这种方式通过组合(持有被适配对象的引用)来实现适配。
C++实现示例:
#include <iostream>
#include <string>
// 目标接口
class CreditCard {
public:
virtual ~CreditCard() = default;
virtual void giveBankDetails() = 0;
virtual std::string getCreditCard() = 0;
};
// 被适配的类
class BankDetails {
private:
std::string bankName;
std::string accHolderName;
long accNumber;
public:
void setBankName(const std::string& bankName) {
this->bankName = bankName;
}
void setAccHolderName(const std::string& accHolderName) {
this->accHolderName = accHolderName;
}
void setAccNumber(long accNumber) {
this->accNumber = accNumber;
}
std::string getBankName() const {
return bankName;
}
std::string getAccHolderName() const {
return accHolderName;
}
long getAccNumber() const {
return accNumber;
}
};
// 适配器(代理对象)
class BankCustomer : public CreditCard {
private:
BankDetails bankDetails;
public:
void giveBankDetails() override {
bankDetails.setAccHolderName("John Doe");
bankDetails.setAccNumber(123456789);
bankDetails.setBankName("ABC Bank");
}
std::string getCreditCard() override {
return "Credit card issued for " + bankDetails.getAccHolderName() +
" from " + bankDetails.getBankName() +
" with account number " + std::to_string(bankDetails.getAccNumber());
}
};
// 使用示例
int main() {
CreditCard* targetInterface = new BankCustomer();
targetInterface->giveBankDetails();
std::cout << targetInterface->getCreditCard() << std::endl;
delete targetInterface;
return 0;
}
3. 参数化适配器方式
这种方式通过构造函数或方法参数传入被适配的对象,实现更灵活的适配。
C++实现示例:
#include <iostream>
#include <memory>
// 目标接口
class Temperature {
public:
virtual ~Temperature() = default;
virtual double getTemperature() const = 0;
};
// 被适配的类
class CelsiusThermometer {
private:
double temperature;
public:
explicit CelsiusThermometer(double temp) : temperature(temp) {}
double getCelsiusTemperature() const {
return temperature;
}
};
// 适配器
class TemperatureAdapter : public Temperature {
private:
std::shared_ptr<CelsiusThermometer> thermometer;
bool toFahrenheit;
public:
TemperatureAdapter(std::shared_ptr<CelsiusThermometer> thermo, bool toFahr)
: thermometer(thermo), toFahrenheit(toFahr) {}
double getTemperature() const override {
if (toFahrenheit) {
// 摄氏转华氏
return (thermometer->getCelsiusTemperature() * 9.0 / 5.0) + 32.0;
}
return thermometer->getCelsiusTemperature();
}
};
// 使用示例
int main() {
auto celsiusThermometer = std::make_shared<CelsiusThermometer>(25.0);
std::unique_ptr<Temperature> celsius =
std::make_unique<TemperatureAdapter>(celsiusThermometer, false);
std::cout << "Temperature in Celsius: " << celsius->getTemperature() << std::endl;
std::unique_ptr<Temperature> fahrenheit =
std::make_unique<TemperatureAdapter>(celsiusThermometer, true);
std::cout << "Temperature in Fahrenheit: " << fahrenheit->getTemperature() << std::endl;
return 0;
}
适配器模式的关键点
目标接口(Target): 客户端期望的接口
被适配者(Adaptee): 需要被适配的现有接口
适配器(Adapter): 将Adaptee接口转换为Target接口
三种实现方式的比较
方式 优点 缺点 适用场景
抽象接口(类适配器) 适配器可以重写被适配者的行为 需要多重继承(C++支持) 需要适配少量不兼容接口
代理对象(对象适配器) 更灵活,可以适配多个不同的被适配者 需要额外的间接层 需要适配多个不兼容接口
参数化适配器 最灵活,可以在运行时配置适配行为 接口可能变得复杂 需要运行时决定适配行为
实际应用场景
遗留系统集成:将新系统与旧系统集成时,可以使用适配器模式使旧系统的接口符合新系统的要求。
第三方库适配:当使用第三方库时,如果其接口不符合你的需求,可以使用适配器模式。
接口标准化:当有多个类实现相似功能但接口不同时,可以使用适配器模式提供统一的接口。
在C++中实现适配器模式时,需要注意资源管理(使用智能指针等RAII技术)和多线程安全性等问题。