23种设计模式 - 适配器模式(Adapter)

适配器模式(Adapter)------ 转接头大法

大白话解释

你现在有两样东西:

一个是你现在系统要用的接口(标准)

一个是别人给你的老东西(不符合标准)

这俩对不上,直接用不了

怎么办?

👉 中间加一层"翻译官"

你说:"给我 info()"

翻译官转头说:"老系统,用 level=1 写日志!"

适配器模式:你不用改老代码,也不用改新系统,中间帮你"翻译一下"就能用了。

常见场景:

  • 老系统接口对接新系统(遗留代码改造)
  • 第三方库接口不符合项目规范
  • 数据格式转换(XML 转 JSON)
  • 日志库适配

核心思路

  1. 目标接口:调用方期望的接口
  2. 被适配者:已有的、不兼容的类
  3. 适配器:包装被适配者,实现目标接口

C++ 代码示例

场景:新系统用的是 Logger 接口,但公司有个老的日志库只有 OldFileLogger,接口不一样。用适配器让老库接入新系统。

cpp 复制代码
#include <iostream>
#include <string>
#include <memory>

// ==============================
// 目标接口:新系统期望的日志接口
// ==============================
class Logger {
public:
    virtual ~Logger() = default;
    virtual void info(const std::string& msg) = 0;
    virtual void warn(const std::string& msg) = 0;
    virtual void error(const std::string& msg) = 0;
};

// ==============================
// 被适配者:老的第三方日志库
// 我们无法修改它的代码!
// ==============================
class OldFileLogger {
public:
    void writeLog(int level, const std::string& content) {
        std::string levelStr;
        switch (level) {
            case 1: levelStr = "INFO"; break;
            case 2: levelStr = "WARN"; break;
            case 3: levelStr = "ERROR"; break;
            default: levelStr = "UNKNOWN";
        }
        std::cout << "[OldFileLogger][" << levelStr << "] " << content << "\n";
    }
};

// ==============================
// 对象适配器(推荐):组合方式
// ==============================
class OldLoggerAdapter : public Logger {
private:
    std::shared_ptr<OldFileLogger> oldLogger; // 持有老系统实例

public:
    OldLoggerAdapter(std::shared_ptr<OldFileLogger> logger)
        : oldLogger(logger) {}

    void info(const std::string& msg) override {
        oldLogger->writeLog(1, msg); // 翻译:info -> level 1
    }

    void warn(const std::string& msg) override {
        oldLogger->writeLog(2, msg); // 翻译:warn -> level 2
    }

    void error(const std::string& msg) override {
        oldLogger->writeLog(3, msg); // 翻译:error -> level 3
    }
};

// ==============================
// 另一个例子:类适配器(继承方式)
// ==============================
class AnotherOldSystem {
public:
    void printMessage(const std::string& msg) {
        std::cout << "[AnotherSystem] >>> " << msg << "\n";
    }
};

// 类适配器:同时继承目标接口和被适配者
class ClassAdapter : public Logger, private AnotherOldSystem {
public:
    void info(const std::string& msg) override {
        printMessage("INFO: " + msg);
    }
    void warn(const std::string& msg) override {
        printMessage("WARN: " + msg);
    }
    void error(const std::string& msg) override {
        printMessage("ERROR: " + msg);
    }
};

// ==============================
// 业务代码:只依赖 Logger 接口
// ==============================
class UserService {
private:
    std::shared_ptr<Logger> logger;

public:
    UserService(std::shared_ptr<Logger> log) : logger(log) {}

    void login(const std::string& username) {
        logger->info("用户登录: " + username);
    }

    void loginFailed(const std::string& username) {
        logger->warn("登录失败: " + username);
    }

    void criticalError(const std::string& msg) {
        logger->error("严重错误: " + msg);
    }
};

int main() {
    std::cout << "=== 使用对象适配器接入老日志库 ===\n";
    auto oldLogger = std::make_shared<OldFileLogger>();
    auto adapter = std::make_shared<OldLoggerAdapter>(oldLogger);

    UserService service1(adapter);
    service1.login("张三");
    service1.loginFailed("黑客");
    service1.criticalError("数据库连接失败");

    std::cout << "\n=== 使用类适配器接入另一个老系统 ===\n";
    auto classAdapter = std::make_shared<ClassAdapter>();
    UserService service2(classAdapter);
    service2.login("李四");
    service2.criticalError("磁盘空间不足");

    return 0;
}

输出:

复制代码
=== 使用对象适配器接入老日志库 ===
[OldFileLogger][INFO] 用户登录: 张三
[OldFileLogger][WARN] 登录失败: 黑客
[OldFileLogger][ERROR] 严重错误: 数据库连接失败

=== 使用类适配器接入另一个老系统 ===
[AnotherSystem] >>> INFO: 用户登录: 李四
[AnotherSystem] >>> ERROR: 严重错误: 磁盘空间不足

两种适配器对比

对象适配器 类适配器
实现方式 组合(持有被适配者实例) 多继承
灵活性 高(运行时可换) 低(编译期固定)
适用 推荐,C++ 多继承虽支持但复杂 简单场景

优缺点

说明
✅ 优点 复用已有代码,不改原类
✅ 优点 单一职责:转换逻辑集中在适配器
❌ 缺点 有时候适配器层太多会变复杂
❌ 缺点 不如直接修改原代码直接(但有时候不能改)

一句话记忆

适配器模式 = 不改旧东西、不改新接口,中间加个"翻译层",让两边能对话。

相关推荐
geovindu3 小时前
CSharp: Prototype Pattern
开发语言·后端·设计模式·.net·原型模式·创建型模式
ttod_qzstudio3 小时前
【软考设计模式】组合模式:“部分-整体“层次结构与树形对象统一操作精讲
设计模式·组合模式
ttod_qzstudio5 小时前
【软考设计模式】装饰模式:“套娃“式增强与动态职责扩展精讲
设计模式·装饰模式
烬羽6 小时前
MCP 多服务器架构实战:一个 Agent 同时操控地图、浏览器和文件
设计模式·设计
阿pin9 小时前
Android随笔-常用的设计模式
android·设计模式
儒雅的名9 小时前
GoF设计模式——策略模式
设计模式·bash·策略模式
折哥的程序人生 · 物流技术专研18 小时前
第4篇:Lambda 简化策略模式(Java 8+)
java·设计模式·策略模式·函数式编程·lambda·代码简化·扩充系列
似是燕归来20 小时前
嵌入式工程师进阶指南:适配器编程设计模式一
c语言·stm32·适配器模式·嵌入式开发
jsonbro1 天前
手把手带你实现发布订阅模式
前端·vue.js·设计模式
workflower1 天前
室内外配送机器人-应用路径
人工智能·机器学习·设计模式·矩阵·自动化