适配器模式(Adapter Pattern)是一种结构型设计模式,它能使接口不兼容的类可以相互合作。核心思想是通过引入一个适配器类,通过转换接口而非修改实现,将一个类的接口转换成客户端期望的另一个接口,从而解决因接口不匹配而无法直接使用的问题。

一、介绍

核心角色
  1. 目标接口(Target):客户端期望的接口,定义了客户端可以使用的方法。
  2. 适配者(Adaptee):需要被适配的现有接口,其方法与目标接口不兼容。
  3. 适配器(Adapter):实现目标接口,并内部包含适配者的实例,通过转换调用适配者的方法。
适配器模式的适用场景
  1. 集成现有组件
    当需要使用一个已存在的类,但它的接口与系统要求的接口不匹配时(如示例中的旧设备接口)。
  2. 复用遗留代码
    维护旧系统时,需要将新功能与遗留代码集成,而遗留代码的接口无法修改(如第三方库、老旧模块)。
  3. 跨平台兼容
    不同平台提供的功能相同但接口不同(如Windows和Linux的文件操作API),通过适配器统一接口。
  4. 测试与模拟
    单元测试中,需要用模拟对象替代真实对象,但模拟对象的接口与被替代对象不一致时。
优点
  1. 接口兼容性:解决了不同接口之间的冲突,使原本无法协作的类可以一起工作。
  2. 代码复用:无需修改现有类(尤其是第三方库或遗留代码),通过适配实现复用。
  3. 松耦合设计:客户端只依赖目标接口,与适配者类解耦,提高系统灵活性。
  4. 透明性:客户端无需知道适配者的存在,使用方式与目标接口一致(如示例中对新旧设备的调用方式相同)。

二、实现

设备数据格式适配,假设系统中存在两种设备:

  • 旧设备(LegacySensor):输出数据格式为 (温度, 湿度) 的字符串(如 "25.5,60.0")。
  • 新设备(ModernSensor):输出数据格式为结构化的 SensorData(包含温度、湿度成员变量)。
    客户端代码希望统一使用 getSensorData() 方法获取 SensorData 结构,因此需要为旧设备创建适配器,使其接口与新设备一致。
// sensor_adapter.h
#include <string>
#include <sstream>
#include <iostream>

// 目标接口:客户端期望的数据格式和方法
struct SensorData {
    float temperature; // 温度(℃)
    float humidity;    // 湿度(%)
};

class SensorTarget {
public:
    virtual ~SensorTarget() = default;
    virtual SensorData getSensorData() = 0; // 客户端统一调用的方法
};

// 适配者:旧设备(接口不兼容,输出字符串)
class LegacySensor {
public:
    // 旧设备的方法:返回"温度,湿度"格式的字符串
    std::string fetchData() {
        // 模拟硬件读取(实际中可能是从硬件寄存器读取)
        return "25.5,60.0";
    }
};

// 适配器:将旧设备接口转换为目标接口
class LegacySensorAdapter : public SensorTarget {
private:
    LegacySensor* legacySensor; // 包含适配者实例

    // 辅助方法:解析字符串为SensorData
    SensorData parseData(const std::string& data) {
        SensorData result;
        std::stringstream ss(data);
        std::string tempStr, humStr;
        
        // 分割字符串(格式:"温度,湿度")
        std::getline(ss, tempStr, ',');
        std::getline(ss, humStr, ',');
        
        result.temperature = std::stof(tempStr);
        result.humidity = std::stof(humStr);
        return result;
    }

public:
    explicit LegacySensorAdapter(LegacySensor* sensor) : legacySensor(sensor) {}

    // 实现目标接口:调用旧设备方法并转换格式
    SensorData getSensorData() override {
        std::string rawData = legacySensor->fetchData(); // 调用适配者方法
        return parseData(rawData); // 转换格式并返回
    }
};

// 新设备:直接实现目标接口(无需适配)
class ModernSensor : public SensorTarget {
public:
    SensorData getSensorData() override {
        // 模拟硬件读取(直接返回结构化数据)
        return {26.3, 58.5};
    }
};

// 客户端代码:只依赖目标接口,不关心具体设备类型
void printSensorData(SensorTarget* sensor) {
    SensorData data = sensor->getSensorData();
    std::cout << "温度:" << data.temperature << "℃,湿度:" << data.humidity << "%" << std::endl;
}

int main() {
    // 1. 使用新设备(直接实现目标接口)
    ModernSensor modernSensor;
    std::cout << "=== 新设备数据 ===" << std::endl;
    printSensorData(&modernSensor);

    // 2. 使用旧设备(通过适配器转换接口)
    LegacySensor legacySensor;
    LegacySensorAdapter adapter(&legacySensor); // 适配旧设备
    std::cout << "\n=== 旧设备数据(通过适配器) ===" << std::endl;
    printSensorData(&adapter); // 客户端调用方式与新设备完全一致

    return 0;
}
输出结果
=== 新设备数据 ===
温度:26.3℃,湿度:58.5%

=== 旧设备数据(通过适配器) ===
温度:25.5℃,湿度:60%
应用场景
  1. 数据格式转换
    • 不同数据源返回格式适配(如JSON与XML格式转换)。
    • 旧系统的字符串数据与新系统的结构化数据适配(如示例)。
  2. 库/框架集成
    • 集成第三方库时,通过适配器封装库的接口,使调用方式与系统一致(如将Boost库的接口适配为自定义接口)。
    • 不同日志库(如log4cpp、spdlog)的接口统一。
  3. 硬件接口适配
    • 不同传感器的输出接口适配(如示例中的新旧传感器)。
    • 不同型号打印机的驱动接口统一。
  4. 系统迁移
    • 逐步替换旧系统:先用适配器使新旧系统共存,再逐步迁移功能。
    • 从单体系统到微服务的过渡:用适配器封装微服务接口,使旧系统无缝调用。

三、优化

优化点

  1. 支持多适配者适配:一个适配器可同时适配多个接口不兼容的类,统一转换为目标接口。
  2. 双向适配器:不仅能将适配者接口转换为目标接口,还能将目标接口转换为适配者接口,支持双向交互。
  3. 模板化适配:通过模板减少适配器类的重复定义,简化适配逻辑。
  4. 动态适配:运行时动态选择适配策略,无需提前确定适配关系。
  5. 适配链:多个适配器组合形成适配链,处理多层接口转换(如A→B→C)。
// sensor_adapter.h
#include <string>
#include <sstream>
#include <iostream>
#include <memory>
#include <vector>

// 目标接口1:客户端期望的结构化数据接口
struct SensorData {
    float temperature; // 温度(℃)
    float humidity;    // 湿度(%)
    float pressure;    // 气压(kPa)- 扩展字段

    // 便于打印
    void print() const {
        std::cout << "温度:" << temperature << "℃,"
                  << "湿度:" << humidity << "%"
                  << ",气压:" << pressure << "kPa" << std::endl;
    }
};

class SensorTarget {
public:
    virtual ~SensorTarget() = default;
    virtual SensorData getSensorData() = 0; // 获取结构化数据
    virtual void setCalibration(float factor) {} // 校准(默认空实现)
};

// 目标接口2:旧系统期望的字符串接口(用于双向适配)
class LegacyStringTarget {
public:
    virtual ~LegacyStringTarget() = default;
    virtual std::string getRawString() = 0; // 获取原始字符串
};

// ------------------------------
// 适配者1:基础旧传感器(仅温度+湿度,字符串输出)
// ------------------------------
class BasicLegacySensor {
public:
    std::string readBasic() {
        return "25.5,60.0"; // 格式:温度,湿度
    }
    void setOldCalibration(int level) {
        std::cout << "旧传感器校准等级设置为:" << level << std::endl;
    }
};

// ------------------------------
// 适配者2:高级旧传感器(温度+湿度+气压,分号分隔)
// ------------------------------
class AdvancedLegacySensor {
public:
    std::string readAdvanced() {
        return "26.3;58.5;101.3"; // 格式:温度;湿度;气压
    }
};

// ------------------------------
// 适配者3:第三方气象站(函数式接口)
// ------------------------------
namespace ThirdParty {
    void getWeatherData(float& temp, float& hum, float& press) {
        temp = 24.8;
        hum = 62.0;
        press = 100.9; // 直接通过引用返回数据
    }
}

// ------------------------------
// 模板适配器:简化单一适配者的适配逻辑
// ------------------------------
template <typename Adaptee, typename GetterFunc>
class TemplateSensorAdapter : public SensorTarget {
private:
    Adaptee* adaptee;
    GetterFunc getter; // 获取数据的函数对象
    std::function<SensorData(const std::string&)> parser; // 解析函数

public:
    TemplateSensorAdapter(Adaptee* a, GetterFunc g, decltype(parser) p)
        : adaptee(a), getter(std::move(g)), parser(std::move(p)) {}

    SensorData getSensorData() override {
        return parser(getter(adaptee)); // 调用适配者方法并解析
    }
};

// ------------------------------
// 多适配者适配器:同时适配多个旧设备
// ------------------------------
class MultiSensorAdapter : public SensorTarget {
private:
    std::vector<SensorTarget*> adapters; // 包含多个子适配器

public:
    void addAdapter(SensorTarget* adapter) {
        if (adapter) adapters.push_back(adapter);
    }

    // 示例:返回所有设备的平均值
    SensorData getSensorData() override {
        if (adapters.empty()) return {0, 0, 0};

        SensorData avg{};
        for (auto adapter : adapters) {
            auto data = adapter->getSensorData();
            avg.temperature += data.temperature;
            avg.humidity += data.humidity;
            avg.pressure += data.pressure;
        }

        int count = adapters.size();
        avg.temperature /= count;
        avg.humidity /= count;
        avg.pressure /= count;
        return avg;
    }
};

// ------------------------------
// 双向适配器:同时适配新旧接口
// ------------------------------
class BidirectionalAdapter : public SensorTarget, public LegacyStringTarget {
private:
    BasicLegacySensor* legacySensor;

    // 字符串转SensorData
    SensorData strToData(const std::string& s) {
        SensorData data{};
        std::stringstream ss(s);
        std::string temp, hum;
        std::getline(ss, temp, ','), std::getline(ss, hum, ',');
        data.temperature = std::stof(temp);
        data.humidity = std::stof(hum);
        data.pressure = 0; // 旧设备无气压数据
        return data;
    }

    // SensorData转字符串
    std::string dataToStr(const SensorData& d) {
        return std::to_string(d.temperature) + "," + std::to_string(d.humidity);
    }

public:
    explicit BidirectionalAdapter(BasicLegacySensor* s) : legacySensor(s) {}

    // 实现SensorTarget接口(旧→新)
    SensorData getSensorData() override {
        return strToData(legacySensor->readBasic());
    }

    // 实现LegacyStringTarget接口(新→旧)
    std::string getRawString() override {
        // 模拟:将新接口的结构化数据转为旧接口的字符串格式
        return dataToStr(getSensorData());
    }

    // 适配校准方法(新接口→旧接口)
    void setCalibration(float factor) override {
        // 将新接口的浮点校准系数转换为旧接口的整数等级
        legacySensor->setOldCalibration(static_cast<int>(factor * 10));
    }
};

// ------------------------------
// 新设备:直接实现目标接口
// ------------------------------
class ModernSensor : public SensorTarget {
public:
    SensorData getSensorData() override {
        return {27.1, 56.8, 101.5}; // 温度,湿度,气压
    }

    void setCalibration(float factor) override {
        std::cout << "新传感器校准系数设置为:" << factor << std::endl;
    }
};

// 客户端函数1:使用新接口处理数据
void processNewSensor(SensorTarget* sensor) {
    std::cout << "处理新接口数据:";
    sensor->getSensorData().print();
}

// 客户端函数2:使用旧接口处理数据(用于双向适配测试)
void processLegacySensor(LegacyStringTarget* sensor) {
    std::cout << "处理旧接口数据:" << sensor->getRawString() << std::endl;
}

int main() {
    // 1. 模板适配器:适配高级旧传感器(简化适配代码)
    AdvancedLegacySensor advSensor;
    auto advAdapter = TemplateSensorAdapter(
        &advSensor,
        [](AdvancedLegacySensor* s) { return s->readAdvanced(); }, // 获取数据
        [](const std::string& s) { // 解析数据(温度;湿度;气压)
            SensorData data{};
            std::stringstream ss(s);
            std::string temp, hum, press;
            std::getline(ss, temp, ';'), std::getline(ss, hum, ';'), std::getline(ss, press, ';');
            data.temperature = std::stof(temp);
            data.humidity = std::stof(hum);
            data.pressure = std::stof(press);
            return data;
        }
    );
    std::cout << "=== 模板适配器(高级旧传感器) ===" << std::endl;
    processNewSensor(&advAdapter);

    // 2. 模板适配器:适配第三方气象站(函数式接口)
    auto thirdPartyAdapter = TemplateSensorAdapter(
        nullptr, // 无实例,直接调用命名空间函数
        [](void*) { 
            float t, h, p;
            ThirdParty::getWeatherData(t, h, p);
            return std::to_string(t) + "," + std::to_string(h) + "," + std::to_string(p);
        },
        [](const std::string& s) { // 解析第三方数据
            SensorData data{};
            std::stringstream ss(s);
            std::string temp, hum, press;
            std::getline(ss, temp, ','), std::getline(ss, hum, ','), std::getline(ss, press, ',');
            data.temperature = std::stof(temp);
            data.humidity = std::stof(hum);
            data.pressure = std::stof(press);
            return data;
        }
    );
    std::cout << "\n=== 模板适配器(第三方气象站) ===" << std::endl;
    processNewSensor(&thirdPartyAdapter);

    // 3. 多适配者适配器:整合多个设备数据(取平均值)
    MultiSensorAdapter multiAdapter;
    BasicLegacySensor basicSensor;
    BidirectionalAdapter bidirAdapter(&basicSensor);
    ModernSensor modernSensor;

    multiAdapter.addAdapter(&advAdapter);
    multiAdapter.addAdapter(&thirdPartyAdapter);
    multiAdapter.addAdapter(&bidirAdapter);
    multiAdapter.addAdapter(&modernSensor);

    std::cout << "\n=== 多设备适配器(平均值) ===" << std::endl;
    processNewSensor(&multiAdapter);

    // 4. 双向适配器:同时支持新旧接口
    std::cout << "\n=== 双向适配器测试 ===" << std::endl;
    processNewSensor(&bidirAdapter);       // 用新接口调用
    processLegacySensor(&bidirAdapter);    // 用旧接口调用
    bidirAdapter.setCalibration(0.8f);     // 适配校准方法

    return 0;
}
输出结果
=== 模板适配器(高级旧传感器) ===
处理新接口数据:温度:26.3℃,湿度:58.5%,气压:101.3kPa

=== 模板适配器(第三方气象站) ===
处理新接口数据:温度:24.8℃,湿度:62%,气压:100.9kPa

=== 多设备适配器(平均值) ===
处理新接口数据:温度:25.85℃,湿度:59.375%,气压:100.925kPa

=== 双向适配器测试 ===
处理新接口数据:温度:25.5℃,湿度:60%,气压:0kPa
处理旧接口数据:25.5,60.0
旧传感器校准等级设置为:8
优化点说明
  1. 模板化适配(减少重复代码)
    通过 TemplateSensorAdapter 模板类,将适配逻辑(获取数据+解析数据)通过函数对象注入,避免为每个适配者编写单独的适配器类。例如:

    • 适配 AdvancedLegacySensor 时,只需传入获取数据的lambda和解析字符串的lambda。
    • 适配第三方库的函数式接口(ThirdParty::getWeatherData)时,无需修改原函数,直接通过模板适配。
  2. 多适配者适配(整合多源数据)
    MultiSensorAdapter 可聚合多个适配器,统一对外提供目标接口。示例中计算多个设备数据的平均值,适合需要整合多源异构数据的场景(如气象站网络、分布式传感器系统)。

  3. 双向适配(支持新旧系统互操作)
    BidirectionalAdapter 同时实现 SensorTarget(新接口)和 LegacyStringTarget(旧接口),既可以将旧设备数据转换为新格式,也可以将新格式数据转换为旧格式,解决了新旧系统双向交互的问题。例如:

    • 新系统可通过 getSensorData() 获取旧设备数据。
    • 旧系统可通过 getRawString() 获取新格式转换后的旧格式数据。
    • 校准方法 setCalibration 也被适配(浮点系数→整数等级)。
  4. 接口扩展与兼容性
    目标接口 SensorData 扩展了气压字段,适配器通过合理默认值(如旧设备气压设为0)保持兼容性,无需修改旧设备代码。

  5. 灵活性与扩展性
    新增适配者时,无需修改现有适配器框架,只需:

    • 对简单适配场景,使用 TemplateSensorAdapter 传入获取和解析逻辑。
    • 对复杂场景,继承 SensorTarget 实现自定义适配器。
适用场景扩展
  • 企业系统集成:整合多个部门的异构系统(如ERP、CRM),通过多适配者适配器统一数据格式。
  • API版本兼容:为不同版本的API提供双向适配器,既支持旧版本调用新版本,也支持新版本兼容旧版本。
  • 跨语言交互:通过适配器转换C++与Python/Java的接口(如用SWIG生成的接口适配自定义接口)。
  • 设备网关:工业物联网中,适配不同协议的传感器(如Modbus、MQTT),统一数据采集接口。
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐