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

优缺点

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

一句话记忆

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

相关推荐
MarkHD4 小时前
RPA工程化实践:三种核心设计模式让复杂流程优雅可控
linux·设计模式·rpa
AI大法师5 小时前
字标Logo设计指南:中文品牌如何用字体做出高级感与辨识度
人工智能·设计模式
Yu_Lijing6 小时前
基于C++的《Head First设计模式》笔记——中介者模式
笔记·设计模式·中介者模式
程序员小寒6 小时前
JavaScript设计模式(四):发布-订阅模式实现与应用
开发语言·前端·javascript·设计模式
是糖糖啊8 小时前
Agent 不好用?先别怪模型,试试 Harness Engineering
人工智能·设计模式
jiankeljx8 小时前
Spring Boot 经典九设计模式全览
java·spring boot·设计模式
WarrenMondeville8 小时前
5.Unity面向对象-依赖倒置原则
unity·设计模式·依赖倒置原则
加个鸡腿儿1 天前
从"包裹器"到"确认按钮"——一个组件的三次重构
前端·vue.js·设计模式
ALex_zry1 天前
现代C++设计模式实战:从AIDC项目看工业级代码架构
c++·设计模式·架构