当前位置: 首页 > news >正文

适配器模式及优化

适配器模式(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; // 包含适配者实例// 辅助方法:解析字符串为SensorDataSensorData 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;// 字符串转SensorDataSensorData 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),统一数据采集接口。
http://www.dtcms.com/a/320349.html

相关文章:

  • 在NVIDIA Orin上用TensorRT对YOLO12进行多路加速并行推理时内存泄漏 (中)
  • linux系统编程
  • 使用winsw把SpringBoot项目注册成window服务
  • javaweb开发之会话_过滤器_监听器
  • 【感知机】感知机(perceptron)学习算法的收敛性
  • 【Unity3D实例-功能-镜头】第三人称视觉-镜头优化
  • 基于深度学习的污水新冠RNA测序数据分析系统
  • Linux机器可直接使用的自动化编译文件
  • AGV_ads通讯exe的创建
  • Java日志技术:从基础到实战
  • 蒙文OCR识别技术难点实现及应用场景剖析
  • Transformer:Attention is all you need
  • HCIP | BGP综合实验报告册
  • PMP项目管理:理解PMP、PMP学什么 / 适合谁学 / Project Management Professional / 项目管理专业人士
  • uat是什么
  • Day32--动态规划--509. 斐波那契数,70. 爬楼梯,746. 使用最小花费爬楼梯
  • 华为服务器如何部署Mindie镜像
  • 俄文识别技术,高精度识别,支持多场景多平台
  • 天猫商品评论API技术指南
  • 如何在NVIDIA H100 GPU上用Ollama以最高性能运行大语言模型
  • 2025数字马力一面面经(社)
  • 【2025最新版】火狐浏览器(官方版)安装-附教程
  • Ubuntu 22 下脚本登录MFA堡垒机
  • 一个自动定位并查询天气的工具(c语言)
  • 八股文智力题
  • 目标检测数据集 - 高架视角道路车辆检测数据集下载「包含VOC、COCO、YOLO三种格式」
  • 为什么会有反射
  • js中的设计模式
  • UnivNet论文分析(20210615)
  • Flutter报错...Unsupported class file major version 65