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

优缺点

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

一句话记忆

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

相关推荐
码匠许师傅1 小时前
【设计模式精讲】29.行为型模式总结与对比(Behavioral Patterns Summary)
c++·设计模式·uml
geovindu4 小时前
CSharp: Observer Pattern
开发语言·后端·观察者模式·设计模式·c#·.netcore·行为模式
yuehuaxin0113 小时前
LP8728A/LP8728B 芯茂微|SOP8L 副边反馈 PWM,18‑30W 适配器恒功率补偿方案解析
嵌入式硬件·智能家居·适配器模式·智能硬件
YHHLAI18 小时前
NestJS 后端开发实战:从设计模式到 CRUD 全栈
设计模式
2401_868534781 天前
长远目标 Long-term Goal 老题沿用
设计模式·需求分析
吃饱了得干活1 天前
Java设计模式实战:一个支付模块的重构之旅,层层递进理解设计模式精髓
后端·设计模式·架构
vivo互联网技术2 天前
软件不是从数据开始,而是从现实开始 | KDC 系列 01
设计模式·架构·领域驱动设计
码匠许师傅2 天前
【设计模式精讲】27.模板方法模式(Template Method)
c++·设计模式·uml·模板方法模式
码匠许师傅2 天前
【设计模式精讲】28.访问者模式(Visitor)
java·设计模式·访问者模式
码匠许师傅2 天前
【设计模式精讲】26.策略模式(Strategy)
c++·设计模式·策略模式·uml