适配器模式-结构型

一、适配器模式

1.1、主要目的

兼容 ,将一个类的接口转换成期望的另一个接口,让两个类因接口不匹配,不能协同工作,变得可以一起工作

1.2、分类

适配器模式主要分为三种类型:

  1. 类适配器模式
  • 通过继承目标类,实现适配接口,完成适配
  • 看一些资料使用多重继承 来实现,虽然C++支持多重继承,但不推荐,容易产生经典的菱形继承问题。
  1. 对象适配器模式
  • 通过组合目标类,实现适配接口,完成适配, 这是最常用、最推荐的实现方式
  • 接口适配器模式
    • 当一个接口定义了过多的方法,而某些类只需要实现接口的部分方法,而不需要实现接口的所有方法时,可以定义一个抽象类实现接口,并实现部分方法,其他类继承抽象类,实现需要的方法即可。

二、为什么需要适配器模式

以当前实际背景为例,现在国内大部分软件,操作系统,数据库等都在进行国产化迭代更新,如果不采用适配器模式,直接重构代码,将会耗费大量人力物力,且重构过程中,可能会出现不可预料的问题,导致项目延期,甚至失败,所以,适配器模式应运而生,通过适配器模式,可以快速兼容现有系统,降低重构成本,提高开发效率。

其他应用场景:

  1. 集成遗留系统:新旧系统集成时,接口不兼容
  2. 第三方库适配:使用不兼容接口的第三方库
  3. 接口标准化:统一多个类似但接口不同的组件

三、UML图

3.1、类适配器模式

3.2、对象适配器模式

3.3、接口适配器模式

四、实际应用

4.1、类适配器模式

4.1.1、场景

PostgreSQL数据库 为例,PostgreSQL数据库在适配国产化过程中,需要兼容Oracle数据库 ,而Oracle数据库的接口与PostgreSQL数据库的接口不一致,所以,需要使用适配器模式,将Oracle数据库的接口转换为PostgreSQL数据库的接口。

4.1.2、UNL图

4.1.3、实现

目标接口

cpp 复制代码
// PostgreSQL接口(目标接口)
class PostgreSQLInterface
{
public:
	virtual ~PostgreSQLInterface() = default;
	virtual void connect() = 0;
	virtual void executeQuery(const std::string& query) = 0;
	virtual void close() = 0;
};

被适配者

cpp 复制代码
// Oracle数据库(被适配者)
class OracleDatabase
{
public:
	void oracleConnect()
	{
		std::cout << "Oracle:连接到数据库\n";
	}

	void oracleQuery(const std::string& query)
	{
		std::cout << "Oracle:执行查询: " << query << std::endl;
	}

	void oracleClose()
	{
		std::cout << "Oracle: 关闭数据库连接" << std::endl;
	}
};

类适配器

cpp 复制代码
class OracleToPostgreSQLAdapter : public PostgreSQLInterface, private OracleDatabase
{
public:
	void connect()override
	{
		std::cout << "适配器:调用oracle连接方法\n";

		OracleDatabase::oracleConnect(); 
	}

	void executeQuery(const std::string& query)override
	{
		std::cout << "适配器:转换并执行Oracle查询\n";

		std::string convertedQuery = this->convertQueryToOracleSyntax(query);
		OracleDatabase::oracleQuery(convertedQuery);
	}

	void close()override
	{
		std::cout << "OracleToPostgreSQLAdapter call closed.\n";
		OracleDatabase::oracleClose();
	}

private:
	std::string convertQueryToOracleSyntax(const std::string& postgreSQLQuery)
	{
		// 简单化查询语句的转换
		if (postgreSQLQuery.find("LIMIT") != std::string::npos) {
			return "SELECT * FROM (SELECT t.*, ROWNUM rnum FROM (" +
				postgreSQLQuery.substr(0, postgreSQLQuery.find("LIMIT")) +
				") t WHERE ROWNUM <= 100) WHERE rnum >= 1";
		}
		else {
			return postgreSQLQuery;
		}
	}
};

测试代码

cpp 复制代码
void test1()
{
    std::cout << "================类适配器模式================\n";

    std::unique_ptr<PostgreSQLInterface> db = std::make_unique<OracleToPostgreSQLAdapter>();

    // 使用统一的PostgreSQL 接口
    db->connect();
    db->executeQuery("SELECT * FROM users");
    db->close();
}

4.2、对象适配器模式

4.2.1、场景

一个系统原本使用文件存储,现在需要切换到云存储服务,但云存储服务的API与现有文件系统接口不兼容,所以需要使用适配器模式,将云存储服务的API转换为文件系统的API。

4.2.2、UNL图

4.2.3、实现

目标接口

cpp 复制代码
// 文件系统接口(目标接口)
class FileSystem
{
public:
	virtual ~FileSystem() = default;
	virtual std::string readFile(const std::string& filename) = 0;
	virtual void writeFile(const std::string& filename, const std::string& content) = 0;
	virtual void deleteFile(const std::string& filename) = 0;
};

被适配者

cpp 复制代码
// 云存储服务(被适配者)
class CloudStorage
{
private:
	std::map<std::string, std::string> storage;
public:
	std::string getObject(const std::string& key)
	{
		std::cout << "云存储:获取对象 " << key << std::endl;
		if (storage.find(key) != storage.end()) {
			return storage[key];
		}
		return "";
	}

	void putObject(const std::string& key, const std::string& data)
	{
		std::cout << "云存储:存储对象 " << key << std::endl;
		storage[key] = data;
	} 

	void removeObject(const std::string& key)
	{
		std::cout << "云存储:删除对象 " << key << std::endl;
		storage.erase(key);
	}
};

对象适配器

cpp 复制代码
class CloudStorageAdapter :public FileSystem
{
private:
	std::unique_ptr<CloudStorage> cloudStorage;

public:
	CloudStorageAdapter()
		:cloudStorage{ std::make_unique<CloudStorage>() }
	{

	}

	std::string readFile(const std::string& filename)override
	{
		std::cout << "适配器:将文件读取转换为云存储获取对象\n";

		std::string objectKey = this->convertFilePathToObjectKey(filename);
		return cloudStorage->getObject(objectKey);
	}

	void writeFile(const std::string& filename, const std::string& content) override
	{
		std::cout << "适配器: 将文件写入转换为云存储存储对象" << std::endl;
		std::string objectKey = this->convertFilePathToObjectKey(filename);
		cloudStorage->putObject(objectKey, content);
	}

	void deleteFile(const std::string& filename) override 
	{
		std::cout << "适配器: 将文件删除转换为云存储删除对象" << std::endl;
		std::string objectKey = this->convertFilePathToObjectKey(filename);
		cloudStorage->removeObject(objectKey);
	}

private:
	std::string convertFilePathToObjectKey(const std::string& filePath)
	{
		return "/home/user/" + filePath;
	}
};

测试代码

cpp 复制代码
void test2()
{
    std::cout << "======================对象适配器模式====================\n";

    std::unique_ptr<FileSystem> fs = std::make_unique<CloudStorageAdapter>();

    fs->writeFile("document/new.txt", "aabbccdd");
    std::string content = fs->readFile("document/new.txt");
    std::cout << "content: " << content << std::endl;

    fs->deleteFile("document/new.txt");
}

4.3、接口适配器模式

4.3.1、场景

一个类定义了许多方法,但只需要其中的几个方法。为避免实现类需要实现所有方法,可以使用接口适配器模式,创建一个适配器类,只实现需要的方法。

4.3.2、UNL图

4.3.3、实现

定义过多的方法

cpp 复制代码
class DataChangeListener 
{
public:
    virtual ~DataChangeListener() = default;

    // 数据变化事件
    virtual void onDataAdded(const std::string& data) = 0;
    virtual void onDataUpdated(const std::string& data) = 0;
    virtual void onDataDeleted(const std::string& data) = 0;
    virtual void onDataMoved(const std::string& from, const std::string& to) = 0;
    virtual void onDataCopied(const std::string& src, const std::string& dest) = 0;
    virtual void onDataRenamed(const std::string& oldName, const std::string& newName) = 0;

    // 系统事件
    virtual void onError(const std::string& error) = 0;
    virtual void onWarning(const std::string& warning) = 0;
    virtual void onInfo(const std::string& info) = 0;

    // 状态事件
    virtual void onStart() = 0;
    virtual void onStop() = 0;
    virtual void onPause() = 0;
    virtual void onResume() = 0;
};

定义一个适配器类,为所有方法提供空实现

cpp 复制代码
class DataChangeAdapter : public DataChangeListener 
{
public:
    // 数据变化事件 - 默认空实现
    void onDataAdded(const std::string& data) override {}
    void onDataUpdated(const std::string& data) override {}
    void onDataDeleted(const std::string& data) override {}
    void onDataMoved(const std::string& from, const std::string& to) override {}
    void onDataCopied(const std::string& src, const std::string& dest) override {}
    void onDataRenamed(const std::string& oldName, const std::string& newName) override {}

    // 系统事件 - 默认空实现
    void onError(const std::string& error) override {}
    void onWarning(const std::string& warning) override {}
    void onInfo(const std::string& info) override {}

    // 状态事件 - 默认空实现
    void onStart() override {}
    void onStop() override {}
    void onPause() override {}
    void onResume() override {}
};

具体实现类1

cpp 复制代码
// 具体实现1:只关心数据添加和删除
class SimpleDataLogger : public DataChangeAdapter 
{
public:
    void onDataAdded(const std::string& data) override {
        std::cout << "[日志] 数据添加: " << data << std::endl;
    }

    void onDataDeleted(const std::string& data) override {
        std::cout << "[日志] 数据删除: " << data << std::endl;
    }

    void onError(const std::string& error) override {
        std::cout << "[日志] 错误: " << error << std::endl;
    }
};

具体实现类2

cpp 复制代码
// 具体实现2:只关心数据更新
class DataUpdateMonitor : public DataChangeAdapter 
{
public:
    void onDataUpdated(const std::string& data) override {
        std::cout << "[监控] 数据更新: " << data << std::endl;
        // 可以触发相关业务逻辑
        notifyRelatedSystems(data);
    }

    void onStart() override {
        std::cout << "[监控] 监控器启动" << std::endl;
    }

    void onStop() override {
        std::cout << "[监控] 监控器停止" << std::endl;
    }

private:
    void notifyRelatedSystems(const std::string& data) {
        std::cout << "[监控] 通知相关系统: " << data << std::endl;
    }
};
cpp 复制代码
// 数据管理器---方便于管理多个实现类
class DataManager {
private:
    std::shared_ptr<DataChangeListener> listener;

public:
    void setListener(std::shared_ptr<DataChangeListener> listener) {
        this->listener = listener;
    }

    void addData(const std::string& data) {
        std::cout << "数据管理器: 添加数据 '" << data << "'" << std::endl;
        if (listener) listener->onDataAdded(data);
    }

    void updateData(const std::string& data) {
        std::cout << "数据管理器: 更新数据 '" << data << "'" << std::endl;
        if (listener) listener->onDataUpdated(data);
    }

    void deleteData(const std::string& data) {
        std::cout << "数据管理器: 删除数据 '" << data << "'" << std::endl;
        if (listener) listener->onDataDeleted(data);
    }

    void start() {
        std::cout << "数据管理器: 启动" << std::endl;
        if (listener) listener->onStart();
    }

    void stop() {
        std::cout << "数据管理器: 停止" << std::endl;
        if (listener) listener->onStop();
    }
};

测试代码

cpp 复制代码
void test3()
{
    std::cout << "============== 接口适配器模式 ============" << std::endl;

    DataManager manager;

    std::cout << "\n1. 使用简单日志监听器:" << std::endl;
    auto simpleLogger = std::make_shared<SimpleDataLogger>();
    manager.setListener(simpleLogger);
    manager.start();
    manager.addData("测试数据1");
    manager.deleteData("测试数据1");
    manager.stop();

    std::cout << "\n2. 使用数据更新监控器:" << std::endl;
    auto updateMonitor = std::make_shared<DataUpdateMonitor>();
    manager.setListener(updateMonitor);
    manager.start();
    manager.updateData("测试数据2");
    manager.stop();
}

五、总结

5.1、优点

  • 单一职责原则:将接口转换代码从业务逻辑中分离

  • 开闭原则:可以在不修改现有代码的情况下引入新的适配器

  • 提高代码复用性:可以复用已有的类

  • 提高灵活性:可以动态切换不同的适配器

5.2、缺点

  • 代码复杂度增加:需要增加新的类和接口

  • 增加系统理解难度:需要理解适配器模式的工作原理

  • 增加系统运行时开销:需要创建新的对象和接口

5.3、三种适配器进行比较

类适配器 对象适配器 接口适配器
实现方式 多重继承 组合 继承抽象类
灵活性 较低 较高 中等
耦合度 较高 较低 中等
推荐度 不推荐 推荐 视情况而定
适用场景 适配器需要同时是目标和被适配者 需要适配多个对象 接口方法过多,只使用部分

5.4、提示

实际项目中,遇到的往往是有实现的抽象类,而不是纯虚接口;此时,可以使用对象适配器模式 ,将抽象类作为适配器,将具体实现类作为被适配者,通过组合的方式实现适配;还可以直接继承;如果时间充裕,可以考虑重构代码,考虑之前的设计是否合理。

相关推荐
进击的小头21 小时前
结构型模式:适配器模式(C语言实现与底层实战)
c语言·适配器模式
数据与后端架构提升之路7 天前
TeleTron 源码揭秘:如何用适配器模式“无缝魔改” Megatron-Core?
人工智能·python·适配器模式
会员果汁15 天前
13.设计模式-适配器模式
设计模式·适配器模式
a35354138218 天前
设计模式-适配器模式
设计模式·适配器模式
阿闽ooo1 个月前
深入浅出适配器模式:从跨国插头适配看接口兼容的艺术
c++·设计模式·适配器模式
JavaBoy_XJ1 个月前
结构型-适配器模式
适配器模式
老朱佩琪!1 个月前
Unity适配器模式
unity·设计模式·游戏引擎·适配器模式
有一个好名字1 个月前
设计模式-适配器模式
设计模式·适配器模式
ZouZou老师2 个月前
C++设计模式之适配器模式:以家具生产为例
java·设计模式·适配器模式