适配器模式(Adapter)------ 转接头大法
大白话解释
你现在有两样东西:
一个是你现在系统要用的接口(标准)
一个是别人给你的老东西(不符合标准)
这俩对不上,直接用不了
怎么办?
👉 中间加一层"翻译官"
你说:"给我 info()"
翻译官转头说:"老系统,用 level=1 写日志!"
适配器模式:你不用改老代码,也不用改新系统,中间帮你"翻译一下"就能用了。
常见场景:
- 老系统接口对接新系统(遗留代码改造)
- 第三方库接口不符合项目规范
- 数据格式转换(XML 转 JSON)
- 日志库适配
核心思路
- 目标接口:调用方期望的接口
- 被适配者:已有的、不兼容的类
- 适配器:包装被适配者,实现目标接口
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++ 多继承虽支持但复杂 | 简单场景 |
优缺点
| 说明 | |
|---|---|
| ✅ 优点 | 复用已有代码,不改原类 |
| ✅ 优点 | 单一职责:转换逻辑集中在适配器 |
| ❌ 缺点 | 有时候适配器层太多会变复杂 |
| ❌ 缺点 | 不如直接修改原代码直接(但有时候不能改) |
一句话记忆
适配器模式 = 不改旧东西、不改新接口,中间加个"翻译层",让两边能对话。