一、模式定义与核心思想
适配器模式是一种 结构型设计模式,其核心作用是 将不兼容的接口转换为客户端期望的接口,如同现实世界中的电源转接器。该模式通过创建 中间转换层 解决接口不匹配问题,实现不同系统间的协同工作。
二、模式结构解析
角色组成:
- 目标接口(Target)
- 客户端期望使用的接口
- 抽象类或接口形式存在
- 适配者(Adaptee)
- 需要被适配的已有组件
- 通常包含有用功能但接口不兼容
- 适配器(Adapter)
- 实现目标接口的转换器
- 封装对适配者的调用
UML图示:
[Client] --> [Target]
^
|
[Adapter] --> [Adaptee]
三、C++ 实现方式对比
1. 类适配器(通过多重继承)
// 旧版温度传感器(华氏度)
class FahrenheitSensor {
public:
float getFahrenheit() const {
return 75.2f; // 返回华氏温度
}
};
// 目标接口(摄氏度)
class CelsiusSensor {
public:
virtual ~CelsiusSensor() = default;
virtual float getTemperature() const = 0;
};
// 类适配器(多重继承)
class SensorAdapter : public CelsiusSensor,
private FahrenheitSensor {
public:
float getTemperature() const override {
// 转换公式:C = (F - 32) * 5/9
return (getFahrenheit() - 32) * 5 / 9;
}
};
特点:
- 通过继承获得适配者功能
- 需要支持多重继承的语言
- 静态绑定,缺乏灵活性
2. 对象适配器(通过组合方式)
class CelsiusSensor {
public:
virtual ~CelsiusSensor() = default;
virtual float getCelsius() const = 0;
};
class FahrenheitSensor {
public:
float getFahrenheit() const { return 75.2f; }
};
class SensorAdapter : public CelsiusSensor {
std::unique_ptr<FahrenheitSensor> adaptee;
public:
SensorAdapter(std::unique_ptr<FahrenheitSensor> sensor)
: adaptee(std::move(sensor)) {}
float getCelsius() const override {
return (adaptee->getFahrenheit() - 32) * 5 / 9;
}
};
特点:
- 使用组合方式持有适配者
- 支持运行时动态适配
- 更符合组合优于继承原则
四、实际应用场景案例
案例:第三方支付接口适配
// 现有系统接口(目标接口)
class PaymentProcessor {
public:
virtual void process(double amount) = 0;
virtual ~PaymentProcessor() = default;
};
// 第三方支付SDK(不兼容接口)
class AlipaySDK {
public:
void sendPayment(double yuan) {
std::cout << "支付宝支付: " << yuan << " 元\n";
}
};
// 适配器实现
class AlipayAdapter : public PaymentProcessor {
AlipaySDK alipay;
static constexpr double EXCHANGE_RATE = 0.15; // 美元兑人民币
public:
void process(double usd) override {
double yuan = usd / EXCHANGE_RATE;
alipay.sendPayment(yuan);
}
};
// 客户端调用
void processPayment(PaymentProcessor& processor) {
processor.process(100.0); // 支付100美元
}
// 使用示例
AlipayAdapter alipay;
processPayment(alipay); // 自动转换为人民币支付
五、模式优势与局限
✅ 核心优势:
- 接口兼容:解决新旧系统对接问题
- 复用现有代码:无需修改原始类
- 单一职责原则:分离接口转换与业务逻辑
- 开闭原则:通过适配器扩展而非修改
❗ 使用注意事项:
- 不要过度使用:优先考虑重构接口的可能性
- 避免多层嵌套:超过3层的适配器说明设计存在问题
- 性能考量:代理调用带来的额外开销
- 维护成本:需要同步维护适配器与原始接口
六、与其他模式的关系
模式名称 | 关联点 | 核心区别 |
---|---|---|
代理模式 | 都包装对象 | 代理提供相同接口,适配器改变接口 |
外观模式 | 简化复杂系统 | 外观定义新接口,适配器复用现有接口 |
装饰器模式 | 都使用组合结构 | 装饰器增强功能,适配器改变接口 |
七、现代C++实现技巧
- 模板适配器(编译期适配)
template <typename Adaptee>
class GenericAdapter : public PaymentProcessor {
Adaptee adaptee;
public:
void process(double amount) override {
// 通用转换逻辑
adaptee.specificProcess(amount * 100);
}
};
- 智能指针管理生命周期
class SensorAdapter : public CelsiusSensor {
std::shared_ptr<FahrenheitSensor> adaptee;
public:
SensorAdapter(std::shared_ptr<FahrenheitSensor> sensor)
: adaptee(std::move(sensor)) {}
// ...
};
- 使用std::function实现动态适配
using PaymentHandler = std::function<void(double)>;
class LambdaAdapter : public PaymentProcessor {
PaymentHandler handler;
public:
LambdaAdapter(PaymentHandler h) : handler(h) {}
void process(double amount) override {
handler(amount);
}
};
// 使用示例
auto wechatAdapter = LambdaAdapter([](double usd) {
std::cout << "微信支付: " << usd * 7.0 << " CNY\n";
});
八、最佳实践建议
-
接口设计阶段:
预留适配器扩展点,使用抽象基类定义接口 -
遗留系统集成:
为每个第三方服务创建独立适配器 -
测试策略:
- 编写适配器接口的Mock测试
- 验证边界值的正确转换(如货币单位)
-
文档规范:
- 明确标注适配器类的作用域
- 记录接口转换规则(如汇率计算公式)
九、实战经验分享
典型错误案例:多层嵌套适配器
// 错误示范:双重适配导致维护噩梦
class A { void foo(int); };
class B { void bar(double); };
class C { void baz(std::string); };
class BadAdapter : public Target {
A a; B b; C c;
public:
void execute() override {
int x = /*...*/;
a.foo(x);
double y = /*...*/;
b.bar(y);
std::string z = /*...*/;
c.baz(z);
}
};
优化方案:
- 使用外观模式封装复杂子系统
- 通过领域驱动设计重构统一接口
适配器模式是系统集成的重要工具,但如同任何强大的工具,需要遵循 "最小适配"原则:只转换必要的接口部分,保持适配器的简洁性。