1. 模式定义
适配器模式(Adapter Pattern)是一种结构型设计模式,它允许不兼容的接口之间能够一起工作。适配器通过包装一个现有的类,将其接口转换为目标接口期望的形式。
2. 核心角色
- 目标接口(Target):客户端期望使用的接口
- 适配者(Adaptee):需要被适配的现有类,具有不兼容的接口
- 适配器(Adapter):实现目标接口,内部持有适配者对象,完成接口转换
- 客户端(Client):使用目标接口的类
3. 工作流程
调用
实现
持有并调用
转换调用
客户端 Client
目标接口 Target
request
适配器 Adapter
request
适配者 Adaptee
specificRequest
4. 实现方式
4.1 类适配器(使用多重继承)
<<interface>>
Target
+request()
Adaptee
+specificRequest()
Adapter
+request()
4.2 对象适配器(使用组合)
持有
<<interface>>
Target
+request()
Adaptee
+specificRequest()
Adapter
-adaptee: Adaptee
+request()
request() {
adaptee.specificRequest()
}
5. C++代码示例
场景:电源适配器(不同电压标准)
cpp
#include <iostream>
#include <memory>
#include <string>
// ========== 目标接口:目标设备期望的接口 ==========
// 中国标准插座(220V)
class ChineseSocket {
public:
virtual ~ChineseSocket() = default;
virtual void charge_220v() const {
std::cout << "使用220V电压充电" << std::endl;
}
};
// ========== 适配者:需要被适配的类 ==========
// 美国标准插座(110V)
class USASocket {
public:
void charge_110v() const {
std::cout << "使用110V电压充电" << std::endl;
}
};
// 日本标准插座(100V)
class JapanSocket {
public:
void charge_100v() const {
std::cout << "使用100V电压充电" << std::endl;
}
};
// ========== 对象适配器(推荐方式) ==========
// 美国到中国的适配器
class USAAdapter : public ChineseSocket {
private:
std::unique_ptr<USASocket> usa_socket_;
public:
USAAdapter(std::unique_ptr<USASocket> socket)
: usa_socket_(std::move(socket)) {}
void charge_220v() const override {
std::cout << "[适配器] 将110V转换为220V: ";
usa_socket_->charge_110v();
}
};
// 日本到中国的适配器
class JapanAdapter : public ChineseSocket {
private:
std::unique_ptr<JapanSocket> japan_socket_;
public:
JapanAdapter(std::unique_ptr<JapanSocket> socket)
: japan_socket_(std::move(socket)) {}
void charge_220v() const override {
std::cout << "[适配器] 将100V转换为220V: ";
japan_socket_->charge_100v();
}
};
// ========== 类适配器(使用多重继承,C++特有) ==========
// 注意:多重继承在某些情况下可能导致歧义,谨慎使用
class MultiInheritAdapter : public ChineseSocket, private USASocket {
public:
void charge_220v() const override {
std::cout << "[类适配器] 将110V转换为220V: ";
charge_110v(); // 调用基类的USASocket方法
}
};
// ========== 客户端代码 ==========
class Client {
public:
void use_chinese_socket(const ChineseSocket& socket) {
socket.charge_220v();
}
};
// ========== 实际应用场景:日志系统适配 ==========
// 场景2:不同的日志库接口适配
// 目标接口:统一的日志接口
class ILogger {
public:
virtual ~ILogger() = default;
virtual void log(const std::string& message) = 0;
virtual void error(const std::string& message) = 0;
};
// 第三方日志库A(不兼容的接口)
class ThirdPartyLoggerA {
public:
void write_log(const std::string& msg) {
std::cout << "[LogA] " << msg << std::endl;
}
void write_error(const std::string& err) {
std::cout << "[LogA ERROR] " << err << std::endl;
}
};
// 第三方日志库B(不兼容的接口)
class ThirdPartyLoggerB {
public:
void output(const std::string& level, const std::string& message) {
std::cout << "[LogB-" << level << "] " << message << std::endl;
}
};
// 适配器:适配LoggerA到标准接口
class LoggerAAdapter : public ILogger {
private:
std::unique_ptr<ThirdPartyLoggerA> logger_;
public:
LoggerAAdapter(std::unique_ptr<ThirdPartyLoggerA> logger)
: logger_(std::move(logger)) {}
void log(const std::string& message) override {
logger_->write_log(message);
}
void error(const std::string& message) override {
logger_->write_error(message);
}
};
// 适配器:适配LoggerB到标准接口
class LoggerBAdapter : public ILogger {
private:
std::unique_ptr<ThirdPartyLoggerB> logger_;
public:
LoggerBAdapter(std::unique_ptr<ThirdPartyLoggerB> logger)
: logger_(std::move(logger)) {}
void log(const std::string& message) override {
logger_->output("INFO", message);
}
void error(const std::string& message) override {
logger_->output("ERROR", message);
}
};
// ========== 主函数演示 ==========
int main() {
std::cout << "========== 电源适配器示例 ==========" << std::endl;
// 创建客户端
Client client;
// 直接使用中国插座
std::cout << "\n1. 直接使用中国插座:" << std::endl;
ChineseSocket chinese_socket;
client.use_chinese_socket(chinese_socket);
// 使用美国插座(通过适配器)
std::cout << "\n2. 使用美国插座(通过适配器):" << std::endl;
auto usa_socket = std::make_unique<USASocket>();
USAAdapter usa_adapter(std::move(usa_socket));
client.use_chinese_socket(usa_adapter);
// 使用日本插座(通过适配器)
std::cout << "\n3. 使用日本插座(通过适配器):" << std::endl;
auto japan_socket = std::make_unique<JapanSocket>();
JapanAdapter japan_adapter(std::move(japan_socket));
client.use_chinese_socket(japan_adapter);
// 使用多重继承适配器
std::cout << "\n4. 使用多重继承适配器:" << std::endl;
MultiInheritAdapter multi_adapter;
client.use_chinese_socket(multi_adapter);
std::cout << "\n========== 日志系统适配器示例 ==========" << std::endl;
// 使用统一的日志接口
std::vector<std::unique_ptr<ILogger>> loggers;
// 添加LoggerA
auto logger_a = std::make_unique<ThirdPartyLoggerA>();
loggers.push_back(std::make_unique<LoggerAAdapter>(std::move(logger_a)));
// 添加LoggerB
auto logger_b = std::make_unique<ThirdPartyLoggerB>();
loggers.push_back(std::make_unique<LoggerBAdapter>(std::move(logger_b)));
// 统一调用日志接口
for (auto& logger : loggers) {
logger->log("这是一条普通日志");
logger->error("这是一条错误日志");
std::cout << std::endl;
}
return 0;
}
6. 异同点详细对比表
| 对比维度 | 类适配器 | 对象适配器 |
|---|---|---|
| 实现方式 | 多重继承(继承Target和Adaptee) | 对象组合(继承Target,持有Adaptee) |
| 耦合度 | 高(编译时绑定,继承关系) | 低(运行时绑定,组合关系) |
| 灵活性 | 低,无法动态改变适配者 | 高,可动态更换适配者对象 |
| 代码复杂度 | 较高(需要处理多重继承问题) | 较低(清晰的组合关系) |
| 内存占用 | 较小(无额外指针) | 较大(需要存储适配者指针) |
| 重写适配者行为 | 容易(通过虚函数重写) | 困难(需要创建适配者子类) |
| 适配多个适配者 | 容易(多继承多个类) | 容易(组合多个对象) |
| 适配适配者子类 | 自动支持(直接继承) | 需要传入具体子类对象 |
| 语言支持 | 需要支持多重继承的语言 | 几乎所有OOP语言都支持 |
| C++特有考虑 | 需注意菱形继承问题 | 无特殊问题 |
7. 应用场景
适配器模式
应用场景
系统集成
新旧系统对接
第三方库集成
API版本兼容
数据转换
不同格式转换
数据结构适配
单位转换
遗留系统改造
封装遗留代码
平滑升级过渡
保持接口稳定
多平台兼容
不同平台API
跨平台开发
硬件抽象层
框架适配
STL容器适配器
智能指针适配
回调函数适配
典型应用场景详解:
-
系统集成
- 新旧系统API不兼容时
- 集成第三方服务或库
- 不同系统间的数据交换
-
遗留系统维护
- 替换或升级部分模块时保持接口不变
- 封装复杂或混乱的遗留代码
-
多平台开发
- 统一不同操作系统的API差异
- 适配不同硬件设备的接口
-
框架和库的使用
- STL中的
std::stack、std::queue(基于deque适配) - 日志框架适配不同的日志库
- 数据库访问层适配不同数据库
- STL中的
8. 优缺点
适配器模式
优点
缺点
提高复用性
重用现有类
增加透明性
客户端无感知
灵活性高
可动态切换适配器
符合开闭原则
不修改原有代码
增加代码复杂度
过多使用可能混乱
系统结构
C++类适配器
需要多重继承
9. 与其他模式的关系
- 桥接模式:适配器用于改变接口,桥接用于分离抽象和实现
- 装饰器模式:适配器改变对象接口,装饰器增强对象功能
- 外观模式:适配器改变单个对象的接口,外观为子系统提供统一接口
- 代理模式:适配器改变接口,代理保持相同接口
10. 最佳实践建议
- 优先使用对象适配器而非类适配器(避免多重继承的复杂性)
- 适配器不应过于复杂,如果转换逻辑复杂,考虑重新设计
- 适度使用,不要为了适配而适配,在设计阶段就考虑接口统一
- 文档化适配逻辑,清晰说明接口转换的规则
适配器模式是一个实用且常用的设计模式,在保持系统整洁和模块化方面发挥着重要作用。