C++ 设计模式——适配者模式

devtools/2024/11/9 17:07:48/

C++ 设计模式——适配者模式

    • C++ 设计模式——适配者模式
      • 1. 主要组成成分
      • 2. 逐步构建适配者模式
        • 2.1 目标抽象类定义
        • 2.2 源类实现
        • 2.3 适配器类实现
        • 2.4 客户端
      • 3. 适配者模式 UML 图
        • 适配者模式 UML 图解析
      • 5. 类适配者
      • 6. 适配者模式的优点
      • 7. 适配者模式的缺点
      • 8. 适配者模式适用场景
      • 总结
      • 完整代码

C++ 设计模式——适配者模式

适配者模式(Adapter Pattern)是一种结构型设计模式,主要用于解决接口不兼容的问题。其核心思想是通过创建一个适配器类,将一个类的接口转换成客户端所期待的另一种接口。

1. 主要组成成分

  • 目标抽象类(Target):这是客户端所期望的接口,定义了客户端使用的方法。
  • 源类(Adaptee):这是需要被适配的类,拥有某些功能,但其接口与目标接口不兼容。
  • 适配器类(Adapter):适配器类实现目标接口,并持有一个源类的实例。在适配器中,适配器会调用源类的方法,以满足目标接口的要求。
  • 客户端(Client):客户端通过目标接口与适配器进行交互,而不是直接与源类交互。

2. 逐步构建适配者模式

以日志系统为例,逐步构建适配者模式。

2.1 目标抽象类定义

目标抽象类LogToDatabase,包含与日志数据库操作相关的方法:初始化数据库、写入日志、读取日志和关闭数据库。

//目标接口
class LogToDatabase
{
public:virtual void initdb() = 0; //不一定非是纯虚函数virtual void writetodb(const char* pcontent) = 0;virtual void readfromdb() = 0;virtual void closedb() = 0;virtual ~LogToDatabase() {} //做父类时析构函数应该为虚函数
};
2.2 源类实现

定义源类 LogToFile,实现日志文件操作的方法,这些方法与目标接口不兼容,需要通过适配器转换。

//源类
class LogToFile
{
public:void initfile(){//做日志文件初始化工作,比如打开文件等等//......}void writetofile(const char* pcontent){//将日志内容写入文件//......}void readfromfile(){//从日志中读取一些信息//......}void closefile(){//关闭日志文件//......}//......可能还有很多其他成员函数,略
};
2.3 适配器类实现

实现适配器类 LogAdapter,它继承自目标接口 LogToDatabase。构造函数接受 LogToFile 对象的指针,实现目标接口的方法时调用源类的方法。

//适配器类
class LogAdapter :public LogToDatabase
{
public://构造函数LogAdapter(LogToFile* pfile) //形参是老接口所属类的指针{m_pfile = pfile;}virtual void initdb(){cout << "在LogAdapter::initdb()中适配LogToFile::initfile()" << endl;//这其中也可以加任何的其他代码......m_pfile->initfile();}virtual void writetodb(const char* pcontent){cout << "在LogAdapter::writetodb()中适配LogToFile::writetofile()" << endl;m_pfile->writetofile(pcontent);}virtual void readfromdb(){cout << "在LogAdapter::readfromdb()中适配LogToFile::readfromdb()" << endl;m_pfile->readfromfile();}virtual void closedb(){cout << "在LogAdapter::closedb()中适配LogToFile::closedb()" << endl;m_pfile->closefile();}
private:LogToFile* m_pfile;
};
2.4 客户端

main 函数中,创建 LogToFileLogAdapter 实例,并通过适配器调用目标接口的方法。

//客户端使用
int main()
{LogToFile* plog = new LogToFile();LogToDatabase* plogdb = new LogAdapter(plog);plogdb->initdb();plogdb->writetodb("向数据库中写入一条日志,实际是向日志文件中写入一条日志");plogdb->readfromdb();plogdb->closedb();delete plogdb;delete plog;return 0;
}

3. 适配者模式 UML 图

适配者模式 UML 图

适配者模式 UML 图解析
  • Target (目标抽象类): 此类定义了客户端期望使用的接口,如 initdbwritetodbreadfromdbclosedb 等。这些接口代表了调用者希望利用的未来接口,并将由客户端直接调用。这里指LogToDatabase 类。
  • Adaptee (源类): 这个类扮演被适配的角色,拥有一套已经存在的接口,这些接口与目标接口不兼容需要被适配。适配涉及将调用目标接口的行为转换为对这些已存在接口的调用。在适配者模式中,适配者类可以不止一个,示例中的 LogToFile 类就是这样一个适配者。
  • Adapter (适配器类): 适配器类是适配者模式的核心,它连接 Target 和 Adaptee 角色。此类通过包装一个或多个 Adaptee 对象,使得 Adaptee 的接口看起来像是 Target 的接口。适配器类负责将 Target 接口的调用转换为 Adaptee 接口的调用,实现接口的兼容。在给出的例子中,这个角色由 LogAdapter 类实现。

5. 类适配者

除了上面介绍的对象适配器,还有一种实现方式叫做类适配器。类适配器使用多重继承来实现适配,这在C++中是可行的,但在Java等不支持多重继承的语言中就不能使用。以下是类适配器的实现示例:

类适配器与对象适配器的主要区别在于:类适配器通过继承来实现适配,而对象适配器通过组合来实现适配。类适配器可以重写Adaptee的行为,但也会增加耦合度。

//类适配器
class LogAdapter :public LogToDatabase, private LogToFile
{
public:virtual void initdb(){cout << "在LogAdapter::initdb()中适配LogToFile::initfile()" << endl;//这其中也可以加任何的其他代码......initfile();}virtual void writetodb(const char* pcontent){cout << "在LogAdapter::writetodb()中适配LogToFile::writetofile()" << endl;writetofile(pcontent);}virtual void readfromdb(){cout << "在LogAdapter::readfromdb()中适配LogToFile::readfromdb()" << endl;readfromfile();}virtual void closedb(){cout << "在LogAdapter::closedb()中适配LogToFile::closedb()" << endl;closefile();}
};
// 客户端
int main()
{LogToDatabase* plogdb = new LogAdapter();plogdb->initdb();plogdb->writetodb("向数据库中写入一条日志,实际是向日志文件中写入一条日志");plogdb->readfromdb();plogdb->closedb();delete plogdb;return 0;
}

6. 适配者模式的优点

  • 灵活性:可以通过添加新的适配器来支持新接口,而无需修改现有代码。
  • 复用性:可复用现有的类,减少了重复代码。
  • 解耦:客户端与源类之间的关系通过适配器解耦,降低了系统的复杂性。

7. 适配者模式的缺点

  • 增加复杂性:引入适配器类可能使系统结构变得更加复杂。
  • 性能开销:适配器增加了一层间接调用,可能影响性能,但在大多数情况下影响微乎其微。

8. 适配者模式适用场景

  • 已存在的类的接口不符合系统的需求:当系统需要使用现有的类,但这些类的接口不符合系统的需求时,可以使用适配者模式来使现有的类与系统接口兼容。
  • 需要创建一个可复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作适配器模式可以提供一个中间层,通过包装一个不兼容的对象,将其接口转换成目标接口,从而使其能与多种不同的对象协同工作。
  • 在需要使用几个现有的子类,但是子类的接口不一致时:可以使用适配者模式来适配这些接口。通过定义一个统一的接口,并在适配器中将调用分派给相应的子类接口,可以使得原本由于接口不兼容而不能一起工作的类可以一起工作。
  • 系统需要使用现有的类,而类的接口不符合系统的需求:例如,系统数据的输入需要特定的格式,而现有的库提供的数据格式与之不符。适配器可以在这两者之间进行转换。
  • 整合多个库或框架时:当使用多个库或框架构建应用程序时,经常会遇到因为接口不兼容而无法一起工作的情况。使用适配器模式可以解决这些库或框架间的接口不兼容问题,使它们可以一起工作。
  • 替换系统中的旧组件时:在软件维护或升级过程中,旧的组件可能需要被更现代或功能更强大的组件替换。如果新组件的接口与系统现有的接口不匹配,适配器模式可以用来适配这些接口,从而允许系统平滑过渡到新的组件,而不需要重写大量的代码。

总结

适配者模式是一种强大而灵活的设计模式,它允许不兼容的接口能够协同工作。通过创建适配器,我们可以复用现有的类,而无需修改其代码。这种模式特别适用于系统集成、旧系统改造或者与第三方库协作的场景。

适配者模式的核心在于:

  1. 识别目标接口和源类之间的差异。
  2. 设计适配器类来桥接这些差异。
  3. 在适配器中实现接口转换逻辑。

在使用适配者模式时,需要注意以下几点:

  1. 确保适配器只处理接口转换,不要在其中添加额外的业务逻辑。
  2. 考虑使用对象适配器还是类适配器,根据实际需求和语言特性来选择。
  3. 当需要适配的类较多时,可以考虑使用工厂模式来创建适配器。

虽然适配者模式可能会增加一些复杂性,但它提供的灵活性和可维护性通常会超过这些缺点。在使用时,应该权衡其利弊,选择最适合特定场景的解决方案。

完整代码

#include <iostream>
#include <vector>
#include <string>
#include <fstream>using namespace std;//日志文件操作相关类
class LogToFile
{
public:void initfile(){//做日志文件初始化工作,比如打开文件等等//......}void writetofile(const char* pcontent){//将日志内容写入文件//......}void readfromfile(){//从日志中读取一些信息//......}void closefile(){//关闭日志文件//......}//......可能还有很多其他成员函数,略
};class LogToDatabase
{
public:virtual void initdb() = 0; //不一定非是纯虚函数virtual void writetodb(const char* pcontent) = 0;virtual void readfromdb() = 0;virtual void closedb() = 0;virtual ~LogToDatabase() {} //做父类时析构函数应该为虚函数
};/*//适配器类
class LogAdapter :public LogToDatabase
{
public://构造函数LogAdapter(LogToFile* pfile) //形参是老接口所属类的指针{m_pfile = pfile;}virtual void initdb(){cout << "在LogAdapter::initdb()中适配LogToFile::initfile()" << endl;//这其中也可以加任何的其他代码......m_pfile->initfile();}virtual void writetodb(const char* pcontent){cout << "在LogAdapter::writetodb()中适配LogToFile::writetofile()" << endl;m_pfile->writetofile(pcontent);}virtual void readfromdb(){cout << "在LogAdapter::readfromdb()中适配LogToFile::readfromdb()" << endl;m_pfile->readfromfile();}virtual void closedb(){cout << "在LogAdapter::closedb()中适配LogToFile::closedb()" << endl;m_pfile->closefile();}
private:LogToFile* m_pfile;
}*/;//类适配器
class LogAdapter :public LogToDatabase, private LogToFile
{
public:virtual void initdb(){cout << "在LogAdapter::initdb()中适配LogToFile::initfile()" << endl;//这其中也可以加任何的其他代码......initfile();}virtual void writetodb(const char* pcontent){cout << "在LogAdapter::writetodb()中适配LogToFile::writetofile()" << endl;writetofile(pcontent);}virtual void readfromdb(){cout << "在LogAdapter::readfromdb()中适配LogToFile::readfromdb()" << endl;readfromfile();}virtual void closedb(){cout << "在LogAdapter::closedb()中适配LogToFile::closedb()" << endl;closefile();}
};int main()
{//    LogToFile* plog = new LogToFile();//    LogToDatabase* plogdb = new LogAdapter(plog);//    plogdb->initdb();//    plogdb->writetodb("向数据库中写入一条日志,实际是向日志文件中写入一条日志");//    plogdb->readfromdb();//    plogdb->closedb();//    delete plogdb;//    delete plog;LogToDatabase* plogdb = new LogAdapter();plogdb->initdb();plogdb->writetodb("向数据库中写入一条日志,实际是向日志文件中写入一条日志");plogdb->readfromdb();plogdb->closedb();delete plogdb;return 0;
}

http://www.ppmy.cn/devtools/102327.html

相关文章

Windows Edge 兼容性问题修复

​ 大家好&#xff0c;我是程序员小羊&#xff01; 前言&#xff1a; Windows Edge 浏览器自2015年发布以来&#xff0c;经历了多次更新与优化&#xff0c;尤其在2020年基于 Chromium 引擎的新版 Edge浏览器发布后&#xff0c;其功能和兼容性大幅提升。然而&#xff0c;尽管如此…

ThinkPHP6异步请求的全面解析

在ThinkPHP6中使用异步请求 在Web应用程序的开发中&#xff0c;经常会需要使用异步请求。异步请求能够在后台执行而不干扰页面的其他操作&#xff0c;提高了用户的体验。而在ThinkPHP6框架中&#xff0c;也提供了方便的异步请求方式&#xff0c;本文将详细介绍如何在ThinkPHP6…

【Electron】Electron学习笔记

1.什么是 Electron&#xff1f; Electron 是一个跨平台桌面应用开发框架&#xff0c;开发者可以利用 HTML、CSS、JavaScript 等Web技术来构建桌面应用程序。它本质上是结合了 Chromium 和 Node.js&#xff0c;目前广泛用于桌面应用程序开发。例如&#xff0c;许多桌面应用都采…

Kafka篇之清理或创建topic

1. kafka清理topic主题 清理topic步骤&#xff1a; step1&#xff1a; ./kafka-topics.sh --bootstrap-server 10.143.167.41:9092,10.143.167.42:9092,10.143.167.43:9092 --delete --topic reflow_data_topic请注意&#xff0c;如果 Kafka 的配置中没有设置 delete.topic.e…

okhttp的WebSocket心跳实现原理

okhttp的WebSocket实现心跳包需要服务端新增协议吗 ‌不需要。‌ OkHttp的WebSocket实现已经内置了心跳包机制&#xff0c;通过PING/PONG帧来维持连接保活。这意味着&#xff0c;OkHttp的WebSocket客户端和服务端在通信过程中&#xff0c;会自动发送PING/PONG帧来检测连接的活…

人工智能 | AutoGPT理念与应用

简介 在 ChatGPT 问世之后&#xff0c;大家很容易就发现其依然具备一些很难解决的问题&#xff0c;比如&#xff1a; Token 超出限制怎么办&#xff1f;&#xff08;目前最新的 GPT4 支持最多 8,192 tokens&#xff09;。如何完全自动化&#xff1f;任务需要多步串联&#xf…

nvme的PRP与SGL

一、PRP和SGL简介 主机也有两种方式来告诉SSD数据所在的内存位置&#xff1a; 一是PRP(Physical Region Page&#xff0c;物理区域页)&#xff1b;二是SGL(Scatter/Gather List&#xff0c;分散/聚集列表)。 二、PRP NVMe把主机端的内存划分为一个一个物理页(Page)&#xff0c;…

突破编程:深入理解C++中的组合模式

突破编程&#xff1a;深入理解C中的组合模式 在C及众多面向对象编程语言中&#xff0c;设计模式是解决问题的经典方案&#xff0c;它们帮助开发者在面对复杂系统设计时&#xff0c;能够遵循一套经过验证的最佳实践。组合模式&#xff08;Composite Pattern&#xff09;是这些设…