C++ 适配器模式(Adapter Pattern)
1. 模式定义与核心思想
适配器模式(Adapter Pattern) 是结构型设计模式 ,用于将不兼容的接口转换成客户端期望的接口,让原本无法协同工作的类可以一起工作。
别名:Wrapper(包装器)
核心一句话
加一层中间转换,不改旧代码、不改新接口,让两者兼容。
现实类比
电源插头转换器:设备用国标插头 → 适配器 → 美标插座。
2. 解决的核心痛点
- 第三方库/旧系统接口与当前系统不匹配
- 遗留代码无法修改,但必须接入新系统
- 多平台、多服务、多SDK接口不统一
- 重构时需要渐进式兼容,不能一刀切
- 希望统一调用风格,屏蔽底层差异
传统方案问题
- 直接改源码:第三方/旧代码不能动
- 到处写转换:重复、混乱、难维护
- 重写实现:成本高、风险大
3. 模式四大角色
| 角色 | 职责 |
|---|---|
| Target(目标接口) | 客户端期望使用的接口 |
| Adaptee(被适配者) | 已存在、但接口不兼容的类 |
| Adapter(适配器) | 实现 Target,包装 Adaptee,做转换 |
| Client(客户端) | 面向 Target 编程,使用适配器 |
调用流程
Client → Target 接口 → Adapter → Adaptee
4. 两种实现方式(C++ 必掌握)
4.1 类适配器(继承实现)
- 方式:公有继承 Target,私有继承 Adaptee
- C++ 依赖多重继承
- 编译期绑定,灵活性较低
cpp
#include <iostream>
#include <string>
using namespace std;
// 目标接口(客户端期望)
class Target {
public:
virtual ~Target() = default;
virtual void request(const string& msg) = 0;
};
// 被适配者(旧接口/第三方)
class Adaptee {
public:
void specificRequest(const char* msg) {
cout << "[Adaptee] " << msg << endl;
}
};
// 类适配器:继承 + 转换
class ClassAdapter : public Target, private Adaptee {
public:
void request(const string& msg) override {
// 接口转换
specificRequest(msg.c_str());
}
};
4.2 对象适配器(组合实现)【工程首选】
- 方式:继承 Target,组合 Adaptee 指针/引用
- 符合组合优于继承
- 运行时可切换被适配者,支持适配多个
cpp
// 对象适配器
class ObjectAdapter : public Target {
private:
Adaptee* adaptee_; // 组合被适配者
public:
ObjectAdapter(Adaptee* adaptee) : adaptee_(adaptee) {}
void request(const string& msg) override {
adaptee_->specificRequest(msg.c_str());
}
};
4.3 两种适配器对比
| 特性 | 类适配器 | 对象适配器 |
|---|---|---|
| 实现 | 多重继承 | 组合 |
| 耦合 | 高 | 低 |
| 灵活性 | 编译期固定 | 运行可切换 |
| 适配多对象 | 难 | 易 |
| 重写方法 | 可 | 不可 |
| 推荐 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
5. C++ 完整工程示例(统一支付系统)
场景
旧支付接口、微信支付、支付宝接口不统一,需要对外提供统一支付接口。
cpp
// ==================== 目标接口(统一支付) ====================
class Payment {
public:
virtual ~Payment() = default;
virtual bool pay(int amount, const string& user) = 0;
};
// ==================== 被适配者1:旧美元支付 ====================
class OldPayment {
public:
bool payInDollar(double amount) {
cout << "旧系统支付:$" << amount << endl;
return true;
}
};
// ==================== 被适配者2:微信支付 ====================
class WeChatPay {
public:
bool doPay(string userId, int money) {
cout << "微信支付:用户" << userId << " 支付" << money << "元" << endl;
return true;
}
};
// ==================== 被适配者3:支付宝 ====================
class AliPay {
public:
bool payTo(string account, int cash) {
cout << "支付宝:账户" << account << " 支付" << cash << "元" << endl;
return true;
}
};
// ==================== 适配器1:旧支付适配 ====================
class OldPaymentAdapter : public Payment {
private:
OldPayment* oldPay_;
public:
OldPaymentAdapter(OldPayment* p) : oldPay_(p) {}
bool pay(int amount, const string&) override {
return oldPay_->payInDollar(amount / 7.0);
}
};
// ==================== 适配器2:微信适配 ====================
class WeChatAdapter : public Payment {
private:
WeChatPay* wx_;
public:
WeChatAdapter(WeChatPay* p) : wx_(p) {}
bool pay(int amount, const string& user) override {
return wx_->doPay(user, amount);
}
};
// ==================== 适配器3:支付宝适配 ====================
class AliPayAdapter : public Payment {
private:
AliPay* ali_;
public:
AliPayAdapter(AliPay* p) : ali_(p) {}
bool pay(int amount, const string& user) override {
return ali_->payTo(user, amount);
}
};
使用示例
cpp
int main() {
// 客户端只面向统一接口 Payment
Payment* p1 = new OldPaymentAdapter(new OldPayment());
Payment* p2 = new WeChatAdapter(new WeChatPay());
Payment* p3 = new AliPayAdapter(new AliPay());
p1->pay(700, "user001");
p2->pay(100, "user001");
p3->pay(200, "user002");
return 0;
}
6. 适配器模式经典应用(C++ 标准库)
C++ STL 内置大量容器适配器,是适配器模式最佳示范:
stack(底层可使用 deque/vector/list)queuepriority_queue
示例:
cpp
#include <stack>
#include <vector>
using namespace std;
// stack 是适配器,底层用 vector
stack<int, vector<int>> st;
st.push(1);
st.pop();
7. 适用场景
✅ 第三方库/SDK 接口不兼容
✅ 旧系统、遗留代码需要接入新架构
✅ 多平台 API 差异需要统一
✅ 统一多个相似服务的接口(支付、消息、存储)
✅ 架构重构、渐进式迁移、兼容过渡
✅ 简化复杂接口,提供易用包装
8. 优点
- 不修改原有代码,符合开闭原则
- 单一职责:只做接口转换
- 解耦:客户端不依赖被适配者
- 高复用:复用已有类,不重复造轮子
- 灵活扩展:新增适配只需加适配器类
9. 缺点
- 增加额外类,系统复杂度略微提升
- 多层转换可能轻微影响性能
- 接口差异过大时不适合(应重构而非适配)
10. C++ 开发最佳实践
- 优先使用对象适配器(组合 > 继承)
- 适配器只做转换,不写业务逻辑
- 用智能指针管理被适配者生命周期
- 适配多个同类服务时,可搭配工厂模式创建适配器
- 转换逻辑集中、清晰、可测试
- 不要为了适配而适配:接口差异巨大应优先重构
11. 常见坑点与避坑
- 类适配器滥用多重继承
→ 优先对象适配器 - 适配器写业务逻辑
→ 违反单一职责 - 被适配者生命周期失控
→ 使用 unique_ptr/shared_ptr - 过度适配
→ 接口差异大应重构
12. 面试高频题(标准答案)
Q1:适配器模式作用?
将不兼容接口转换为目标接口,不改原有代码,让新旧系统/第三方服务协同工作。
Q2:类适配器 vs 对象适配器区别?
类适配器用多重继承 ;对象适配器用组合,更灵活、松耦合、工程首选。
Q3:适配器模式体现哪些设计原则?
- 单一职责
- 开闭原则
- 组合优于继承
- 依赖倒置
Q4:什么时候不用适配器?
接口差异巨大、底层逻辑不匹配,应重构而非适配。
Q5:STL 中哪些是适配器?
stack、queue、priority_queue 都是容器适配器。
13. 适配器在架构中的地位
- 防腐层(ACL):隔离第三方/外部系统污染领域模型
- 微服务网关:统一接口格式
- 跨平台层:屏蔽 Windows/Linux 差异
- 新旧系统迁移:无痛过渡