设计模式之结构型

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++中),耦合度较高

对象适配器(组合方式):
- ✅ 优点:更灵活,可以适配多个不同的被适配者,符合组合优于继承原则
- ✅ 优点:可以在运行时动态切换被适配者
- ❌ 缺点:需要创建被适配者的实例

推荐使用对象适配器,因为它更加灵活且符合现代面向对象设计原则。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gaopeng@step by step

你我共同进步

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值