05.C++设计模式-适配器模式

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容器适配器
智能指针适配
回调函数适配

典型应用场景详解:
  1. 系统集成

    • 新旧系统API不兼容时
    • 集成第三方服务或库
    • 不同系统间的数据交换
  2. 遗留系统维护

    • 替换或升级部分模块时保持接口不变
    • 封装复杂或混乱的遗留代码
  3. 多平台开发

    • 统一不同操作系统的API差异
    • 适配不同硬件设备的接口
  4. 框架和库的使用

    • STL中的std::stackstd::queue(基于deque适配)
    • 日志框架适配不同的日志库
    • 数据库访问层适配不同数据库

8. 优缺点

适配器模式
优点
缺点
提高复用性

重用现有类
增加透明性

客户端无感知
灵活性高

可动态切换适配器
符合开闭原则

不修改原有代码
增加代码复杂度
过多使用可能混乱

系统结构
C++类适配器

需要多重继承

9. 与其他模式的关系

  • 桥接模式:适配器用于改变接口,桥接用于分离抽象和实现
  • 装饰器模式:适配器改变对象接口,装饰器增强对象功能
  • 外观模式:适配器改变单个对象的接口,外观为子系统提供统一接口
  • 代理模式:适配器改变接口,代理保持相同接口

10. 最佳实践建议

  1. 优先使用对象适配器而非类适配器(避免多重继承的复杂性)
  2. 适配器不应过于复杂,如果转换逻辑复杂,考虑重新设计
  3. 适度使用,不要为了适配而适配,在设计阶段就考虑接口统一
  4. 文档化适配逻辑,清晰说明接口转换的规则

适配器模式是一个实用且常用的设计模式,在保持系统整洁和模块化方面发挥着重要作用。

相关推荐
2601_956121975 小时前
二分算法(知识点+题目)
c++·算法
蒙奇·D·路飞-7 小时前
OpenClaw:开源的《合金弹头》精神续作与终极本地部署指南
c++·开源·mfc
董员外7 小时前
RAG 系统进化论(八):Agentic RAG(智能体式 RAG),从回答问题到完成任务
人工智能·后端·设计模式
小星星闪亮登场8 小时前
2026萌新联赛第三场-- (郑州轻工业大学)
数据结构·c++·经验分享·算法·贪心算法·排序算法·深度优先
梓䈑9 小时前
【用 Vibe Coding 实现的 C++17 在线判题系统】前端开发 + Web 自动化测试
前端·c++·ai编程
冻柠檬飞冰走茶9 小时前
PTA基础编程题目集 7-8超速判断(C++语言实现)
开发语言·数据结构·c++·算法
TunerT_TQ9 小时前
Valhalla 静态工程审阅 #018|MMCV 源码证据驱动评测【大厂开源基础设施特辑】
c++·开源·mfc·#计算机视觉·#商汤科技·#openmmlab·#静态源码审计
别动我齐刘海10 小时前
Day6 unitree_G1人形机器人GMR—— MotionInput
c语言·c++·人工智能·学习·机器学习·机器人·github
Henry Zhu12311 小时前
C++17 `weak_from_this` 详解:让对象安全地观察自己
c++
董员外11 小时前
RAG 系统进化论(七):Multimodal RAG(多模态 RAG),当知识存在于表格、图片和页面中
人工智能·后端·设计模式