1、适配器模式
1. 概念
适配器模式允许接口不兼容的类能够相互合作。适配器模式充当两个不兼容接口之间的桥梁,将一个类的接口转换成客户端期望的另一个接口。
它就像现实世界中的电源适配器,让不同国家标准的插头能够插入到不同标准的插座中。
2. 特征
1. 目标接口
客户端期望使用的接口。这是客户端代码所依赖的接口。
2. 被适配者
需要被适配的现有类。它包含有用的功能,但其接口与客户端期望的接口不兼容。
3. 适配器
包装被适配者的对象,并实现目标接口。
在适配器内部,它将客户端的调用转换为对被适配者相应方法的调用。
4. 客户端
使用目标接口与适配器进行交互的代码。
3. 解决了什么问题
问题场景:你有一个现有的类(被适配者),它的功能正是客户端所需要的,但是它的接口与客户端期望的接口不匹配。直接修改现有类的接口可能会破坏现有的代码,或者你根本没有权限修改源代码。
解决方案:创建一个适配器类,它实现了客户端期望的目标接口,并在内部包装被适配者对象。当客户端调用目标接口的方法时,适配器将这些调用转发给被适配者的相应方法,并进行必要的参数转换和数据格式调整。
4. 典型应用场景
使用第三方库或遗留代码:当需要集成第三方库,但其接口与你的代码不兼容时。
系统升级和重构:在新系统中需要重用旧的类,但不想修改旧代码时。
接口标准化:需要将多个具有不同接口的类统一到同一个接口下。
数据格式转换:需要在不同数据格式之间进行转换。
4. 优缺点
优点:
符合单一职责原则:将接口转换代码从业务逻辑中分离出来。
符合开闭原则:可以在不修改现有代码的情况下引入新的适配器。
提高代码复用性:允许重用现有的类,即使其接口不兼容。
解耦客户端与被适配者:客户端只依赖于目标接口,不直接依赖于被适配者。
灵活性:可以创建多个适配器来处理不同的接口转换需求。
缺点:
增加代码复杂度:引入额外的类和接口,增加了系统的复杂性。
性能开销:由于多了一层间接调用,可能会带来轻微的性能损失。
过度使用会导致混乱:如果系统中存在大量适配器,可能会使代码难以理解和维护。
6. C++实现示例
场景:假设有一个现有的第三方日志库,它提供日志功能,但接口与系统期望的接口不兼容。示例 1:类适配器(通过继承实现)
#include <iostream>
#include <string>
#include <memory>
// ------ 被适配者 (Adaptee):第三方日志库 ------
class ThirdPartyLogger {
public:
// 第三方库的接口与我们的系统不兼容
void logMessage(const std::string& message, int severity) {
std::cout << "[ThirdParty] Severity " << severity << ": " << message << std::endl;
}
void debugLog(const std::string& msg) {
std::cout << "[ThirdParty DEBUG]: " << msg << std::endl;
}
};
// ------ 目标接口 (Target Interface):我们系统期望的日志接口 ------
class ILogger {
public:
virtual ~ILogger() = default;
virtual void log(const std::string& message) = 0;
virtual void error(const std::string& message) = 0;
virtual void debug(const std::string& message) = 0;
};
// ------ 适配器 (Adapter):通过继承实现 ------
class LoggerAdapter : public ILogger, private ThirdPartyLogger {
public:
void log(const std::string& message) override {
// 将我们的log调用适配到第三方接口
logMessage(message, 1); // 普通日志级别
}
void error(const std::string& message) override {
// 将error调用适配到第三方接口
logMessage("ERROR: " + message, 3); // 错误级别
}
void debug(const std::string& message) override {
// 将debug调用适配到第三方接口
debugLog("DEBUG: " + message);
}
};
// ------ 客户端代码 ------
void processUserRequest(ILogger& logger) {
logger.debug("Starting user request processing");
// 模拟一些处理逻辑
logger.log("Processing user data");
// 模拟错误
logger.error("Invalid user input detected");
logger.debug("Finished user request processing");
}
int main() {
std::cout << "Adapter Pattern Demo - Class Adapter\n";
std::cout << "====================================\n\n";
LoggerAdapter adapter;
processUserRequest(adapter);
return 0;
}
示例 2:对象适配器(通过组合实现 - 更推荐的方式)
#include <iostream>
#include <string>
#include <memory>
// ------ 被适配者 (Adaptee) ------
class ThirdPartyLogger {
public:
void logMessage(const std::string& message, int severity) {
std::cout << "[ThirdParty] Severity " << severity << ": " << message << std::endl;
}
void debugLog(const std::string& msg) {
std::cout << "[ThirdParty DEBUG]: " << msg << std::endl;
}
// 另一个不兼容的方法
void writeToFile(const std::string& filename, const std::string& content) {
std::cout << "Writing to file " << filename << ": " << content << std::endl;
}
};
// ------ 目标接口 (Target Interface) ------
class ILogger {
public:
virtual ~ILogger() = default;
virtual void log(const std::string& message) = 0;
virtual void error(const std::string& message) = 0;
virtual void debug(const std::string& message) = 0;
virtual void logToFile(const std::string& filename, const std::string& message) = 0;
};
// ------ 适配器 (Adapter):通过组合实现 ------
class ObjectLoggerAdapter : public ILogger {
private:
std::unique_ptr<ThirdPartyLogger> adaptee_;
public:
ObjectLoggerAdapter(std::unique_ptr<ThirdPartyLogger> adaptee)
: adaptee_(std::move(adaptee)) {}
void log(const std::string& message) override {
adaptee_->logMessage(message, 1);
}
void error(const std::string& message) override {
adaptee_->logMessage("ERROR: " + message, 3);
}
void debug(const std::string& message) override {
adaptee_->debugLog("DEBUG: " + message);
}
void logToFile(const std::string& filename, const std::string& message) override {
// 复杂的适配逻辑:将我们的接口调用转换为被适配者的接口
std::string formattedContent = "LOG: " + message;
adaptee_->writeToFile(filename, formattedContent);
}
};
// ------ 另一个被适配者:XML数据处理器 ------
class XmlDataProcessor {
public:
std::string processXml(const std::string& xmlData) {
return "Processed XML: " + xmlData;
}
};
// ------ 另一个目标接口:数据处理器 ------
class IDataProcessor {
public:
virtual ~IDataProcessor() = default;
virtual std::string processData(const std::string& data) = 0;
};
// ------ 另一个适配器:XML到通用数据处理的适配器 ------
class XmlDataAdapter : public IDataProcessor {
private:
std::unique_ptr<XmlDataProcessor> xmlProcessor_;
public:
XmlDataAdapter(std::unique_ptr<XmlDataProcessor> processor)
: xmlProcessor_(std::move(processor)) {}
std::string processData(const std::string& data) override {
// 假设我们需要将普通数据转换为XML格式
std::string xmlData = "<data>" + data + "</data>";
return xmlProcessor_->processXml(xmlData);
}
};
// ------ 客户端代码 ------
void clientCode(ILogger& logger, IDataProcessor& processor) {
std::cout << "\nClient: Using adapted interfaces\n";
logger.debug("Client started");
logger.log("Processing some data");
std::string result = processor.processData("Hello World");
logger.log("Processing result: " + result);
logger.logToFile("output.txt", "Log message for file");
logger.error("An error occurred");
}
int main() {
std::cout << "Adapter Pattern Demo - Object Adapter\n";
std::cout << "=====================================\n\n";
// 创建被适配者对象
auto thirdPartyLogger = std::make_unique<ThirdPartyLogger>();
auto xmlProcessor = std::make_unique<XmlDataProcessor>();
// 创建适配器
ObjectLoggerAdapter loggerAdapter(std::move(thirdPartyLogger));
XmlDataAdapter dataAdapter(std::move(xmlProcessor));
// 客户端使用统一的接口
clientCode(loggerAdapter, dataAdapter);
// 演示适配器的灵活性:可以轻松替换被适配者
std::cout << "\n" << std::string(40, '=') << "\n";
std::cout << "Creating another adapter instance:\n";
auto anotherLogger = std::make_unique<ThirdPartyLogger>();
ObjectLoggerAdapter anotherAdapter(std::move(anotherLogger));
anotherAdapter.log("This is from another adapter instance");
return 0;
}
6. 两种实现方式的比较
类适配器(继承方式):
- ✅ 优点:不需要创建被适配者的实例,代码更简洁
- ❌ 缺点:需要多重继承(在C++中),耦合度较高
对象适配器(组合方式):
- ✅ 优点:更灵活,可以适配多个不同的被适配者,符合组合优于继承原则
- ✅ 优点:可以在运行时动态切换被适配者
- ❌ 缺点:需要创建被适配者的实例
推荐使用对象适配器,因为它更加灵活且符合现代面向对象设计原则。
3万+

被折叠的 条评论
为什么被折叠?



