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++ 多继承虽支持但复杂 简单场景

优缺点

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

一句话记忆

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

相关推荐
IT 行者2 小时前
软件设计模式会不会是制约大模型编程的障碍?
设计模式·ai编程
t***5443 小时前
还有哪些设计模式适合现代C++
开发语言·c++·设计模式
t***5443 小时前
如何在现代C++项目中有效应用这些设计模式
开发语言·c++·设计模式
贵慜_Derek3 小时前
我们能从 DeerFlow 学到哪些优秀的技术架构设计
人工智能·设计模式·架构
Q741_1474 小时前
设计模式之装饰器模式 理论总结 C++代码实战
c++·设计模式·装饰器模式
无籽西瓜a4 小时前
【西瓜带你学设计模式 | 第十八期 - 命令模式】命令模式 —— 请求封装与撤销实现、优缺点与适用场景
java·后端·设计模式·软件工程·命令模式
studyForMokey5 小时前
【Android面试】设计模式专题
android·设计模式·面试
geovindu5 小时前
go: Abstract Factory Pattern
开发语言·后端·设计模式·golang
木斯佳5 小时前
前端八股文面经大全:京东零售JDY前端一面(2026-04-14)·面经深度解析
前端·算法·设计模式·ai·断点续传
郝学胜-神的一滴5 小时前
[系统设计] 新鲜事系统:写扩散与读扩散的实现与对比
java·设计模式·php·软件构建·需求分析·软件设计·系统设计