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

C++设计模式_结构型模式_适配器模式Adapter

从本篇开始记录结构型模式。
结构型模式,该模式关注对象之间的组合关系,旨在解决如何构建灵活且可复用的类和对象结构。共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
适配器(Adapter)模式是一种结构型模式。生活中有很多适配器思想的实际应用,例如,各种转接头(usb转接头、hdmi转接头)、电源适配器(变压器)等,主要是用来解决不同的接口之间的兼容问题。设想一下,220V的市电不能直接给手机充电,引人一个电源适配器,把220V的市电转换为5V的电压,就可以给手机充电了。在软件开发领域,两个类之间也存在这种不兼容的问题,于是,就可以像生活中引人电源适配器那样引入一个适配器角色的类来解决这两个类之间的兼容性问题。

一个简单的例子

下面是一个简单的示例,这个类中只有日志的一些基础操作函数。

class LogToFile
{
public:void initfile(){//做日志文件初始化工作,比如打开文件等等//.....cout << "日志初始化工作完成" << endl;}void writetofile(const char* pcontent){//将日志内容写入文件//......cout << "pcontent 写入到了文件中" << endl;}void readfromfile(){//从日志中读取一些信息//......cout << "从文件中读取了一些信息" << endl;}void closefile(){//关闭日志文件//......cout << "关闭日志文件" << endl;}// ......
};
void test()
{LogToFile logtofile;logtofile.initfile();logtofile.writetofile("hello");logtofile.readfromfile();logtofile.closefile();/*日志初始化工作完成pcontent 写入到了文件中从文件中读取了一些信息关闭日志文件*/
}

随着项目规模的不断增加,要记录的日志信息也逐渐增多,单纯地向日志文件中记录日志信息会导致日志文件膨胀得过大,不方便管理和查看。于是准备对项目中的日志系统进行升级改造,即从原有的将日志信息写入文件改为将日志信息写人到数据库。改造后的新日志系统如下:

class LogToDatabase
{
public:void initdb(){//连接数据库,做一些基本的数据库连接设置等//......cout << "连接数据库" << endl;}void writetodb(const char* pcontent){//将日志内容写入数据库//......cout << "pcontent 写入到了数据库中" << endl;}void readfromdb(){//从数据库中读取一些日志信息//......cout << "从数据库中读取了一些日志信息" << endl;}void closedb(){//关闭到数据库的链接//......cout << "关闭到数据库的链接" << endl;}
};
void test2()
{LogToDatabase logtodatabase;logtodatabase.initdb();logtodatabase.writetodb("hello");logtodatabase.readfromdb();logtodatabase.closedb();/*连接数据库pcontent 写入到了数据库中从数据库中读取了一些日志信息关闭到数据库的链接*/
}

现在遇到一个问题,数据库断电或者电缆出现问题,导致了新的日志系统不能使用了。可以使用LogToFile类来解决上面问题,但是现在项目中所有的位置都使用的是 LogToDatabase类的接口,而LogToFile类接口与LogToDatabase接口又不完全相同,该怎么办呢?现在的解决办法有三种:
1 将所有的接口改为LogToFile类的接口,这种改动较大;
2 在LogToDataBase类中增加新接口来支持对日志文件的读写,也就是把LogToFile 中的接口,在LogToDataBase 中重新实现一遍,这比较麻烦。
3 使用适配器解决。在该模式中,通过引人适配器类,把LogToDatabase类中,诸如对 writeToDb、readfromDb等成员函数的调用转换为对LogToFie类中,诸如对 WriteToFile readfromFile等成员函数的调用, 从而实现接口调用的目的。
适配器方式如下:

class LogToFile
{
public:void initfile(){//做日志文件初始化工作,比如打开文件等等//.....cout << "日志初始化工作完成" << endl;}void writetofile(const char* pcontent){//将日志内容写入文件//......cout << "pcontent 写入到了文件中" << endl;}void readfromfile(){//从日志中读取一些信息//......cout << "从文件中读取了一些信息" << endl;}void closefile(){//关闭日志文件//......cout << "关闭日志文件" << endl;}// ......
};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::readfromfile()" << endl;m_pfile->readfromfile();}virtual void closedb(){cout << "在LogAdapter::closedb()中适配LogToFile::closefile()" << endl;m_pfile->closefile();}private:LogToFile* m_pfile;  // 聚合关系
};void test()
{LogToFile logtofile;LogAdapter logadapter(&logtofile);logadapter.initdb();logadapter.writetodb("hello");logadapter.readfromdb();logadapter.closedb();/*在LogAdapter::initdb()中适配LogToFile::initfile()日志初始化工作完成在LogAdapter::writetodb()中适配LogToFile::writetofile()pcontent 写入到了文件中在LogAdapter::readfromdb()中适配LogToFile::readfromfile()从文件中读取了一些信息在LogAdapter::closedb()中适配LogToFile::closefile()关闭日志文件*/
}

通过代码可以看到,在test中的代码仅仅做了很小的变动,其中对接口,例如initdb、writetodb、readfromdb、closedb后调用更是没有发生改动,通过引人适配器类,实际调用的接口是 LogToFile 类的 initfile,writetofile、readfromfile、closefile。
实际上,适配器的能力简而言之就是能够将对一种接口的调用转为对另一种接口的调用。使用适配器转换两个类时,这两个类必须是相关的两个类。

引入适配器(Adapter)模式

上面的代码完成了新老日志系统的转换。在不改变老日志系统源码的情况下,通过引人适配器,将使用新日志系统的项目与日志系统连接起来,此时,适配器扮演一个中间人的角色,将项目中针对日志系统的接口转换成对应的老日志系统的接口调用,从而达到新接口适配老接的目的,这就是适配模式的工作。
在这里插入图片描述
两个独立的日志系统;
在这里插入图片描述
使用适配器连接起来。
适配器的定义:将一个类的接口转换成客户希望的另外一个接口。该模式使得原本因为接口不兼容而不能一起工作的类可以一起工作(在上述范例中,不一起工作的类指的就是 LogToDatabase和LogToFile类)。适配器模式还有一个别名叫作包装器(Wrapper)。
适配器的UML图:
在这里插入图片描述
【上图中,LogAdaper和LogToFile之间是聚合关系,因为在LogAdaper中保存了LogToFile的指针】
适配器模式中包含3种角色:
1 Target(日标抽象类):该类定义所需要暴露的接口(诸如initdb、writetodbteadfromdb、closedb等)。
2 Adaptee(适配者类): 该类扮演着被适配的角色,其中定义了一个或多个已经存在,的接口(老接口),这些接口需要适配(对其他接口的调用转换成对这些接口的调用)。
3 Adapter(适配器类): 注意英文字母的拼写区别于Adaptee(适配者类)。适配器类是一个包装类,扮演着转换器的角色,是适配器模式的实现核心,用于调用另一个接口(包装适配者)。
适配器模式与装饰模式有类似的地方,两者都使用了类与类之间的组合关系,但两者实现意图是不同的,适配器模式是将原有的接口适配成另外一个接口,而装饰模式是对原有功能的增强,而且无论装饰多少层,装饰模式的调用接口始终不发生改变。

类适配器

适配器模式依据实现方式分为两种:一种是对象适配器,另一种是类适配器。前边是对象适配器,这种实现方式使用的是类与类之间的聚合关系,实现了委托机制。LogAdapter类中包含了LogToFile指针, 可以认为 LogToFile是LogAdapter的一部分。类适配器是通过继承方式来实现接口的适配:

class LogToFile
{
public:void initfile(){//做日志文件初始化工作,比如打开文件等等//.....cout << "日志初始化工作完成" << endl;}void writetofile(const char* pcontent){//将日志内容写入文件//......cout << "pcontent 写入到了文件中" << endl;}void readfromfile(){//从日志中读取一些信息//......cout << "从文件中读取了一些信息" << endl;}void closefile(){//关闭日志文件//......cout << "关闭日志文件" << endl;}// ......
};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 , private LogToFile
{  // private 继承,父类的public protected成员函数在子类中变成private成员函数
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::readfromfile()" << endl;readfromfile();}virtual void closedb(){cout << "在LogAdapter::closedb()中适配LogToFile::closefile()" << endl;closefile();}
};
void test()
{LogAdapter logadapter;logadapter.initdb();logadapter.writetodb("hello");logadapter.readfromdb();logadapter.closedb();/*在LogAdapter::initdb()中适配LogToFile::initfile()日志初始化工作完成在LogAdapter::writetodb()中适配LogToFile::writetofile()pcontent 写入到了文件中在LogAdapter::readfromdb()中适配LogToFile::readfromfile()从文件中读取了一些信息在LogAdapter::closedb()中适配LogToFile::closefile()关闭日志文件*/
}

在这里插入图片描述
从代码中可以看到,LogAdapter使用了多重继承,以public(公有继承)的方式继承
LogToDatabase, public继承所作表的是一种is-a关系,也就是通过子类产生的对象一定也是一个父类对象(子类继承了父类的接口)。同时,LogAdapter 还以 private(protected 也可以)的方式继承了LogToFile类, private继承关系就不是一种is-a关系了,而是一种组合关系,更明确地说,是组合关系中的:implemented-in-terms-of(根据……实现出)关系,这里的private继承就表示想通过LogToFile类实现出LogAdapter的意思。

适配器模式的扩展运用

使用适配器模式并不一定是好事,而是在开发后期不得以才使用这种设计模式。但软件开发中也存在时常要发布新版本的情况,新版本也存在与老版本的兼容性问题,有时完全抛弃老版本并不现实,所以才借助适配器模式使新老版本兼容。在遗留代码的复用、类库的迁移等工作方面,适配器模式仍旧能发挥巨大的作用。
C++标准库中大量使用了适配器,容器适配器,算法适配器,迭代器适配器等。
(1) 容器适配器: 对于容器中的双端队列deque,它既包含了堆栈stack的能力也包含了队列queue的能力,在实现stack和queue 源码时,只需要利用既有的 deque 源码并进行适当的改造(减少一些东西)。因此stack和queue都可以看作容器适配器。

_EXPORT_STD template <class _Ty, class _Container = deque<_Ty>>
class queue {void pop() noexcept(noexcept(c.pop_front())) /* strengthened */ {c.pop_front();}void swap(queue& _Right) noexcept(_Is_nothrow_swappable<_Container>::value) {using _STD swap;swap(c, _Right.c); // intentional ADL}_NODISCARD const _Container& _Get_container() const noexcept {return c;}protected:_Container c{};
};

从源码中可以看出,queue队列的pop() 就是 调用了 deque的pop_front() 方法;’
(2)算法适配器,std::bind(绑定器)就是一个典型的算法适配器;
(3)迭代器适配器:例如reverse_iterator(反向迭代器),其实现只是对迭代器iterator的一层简单封装。

http://www.dtcms.com/a/423015.html

相关文章:

  • 缓存总线是什么?
  • 无锡漫途科技大型平原灌区水量调度一体化智慧监测方案
  • 专注创新,守护安全——新天力科技以领先技术引领食品包装行业
  • ARM芯片架构之DAP:AXI-AP 技术详解
  • ARM芯片架构之调试访问端口(DAP)
  • 企业门户网站管理办法ceo是什么意思是什么职位
  • Java SE “核心类:String/Integer/Object”面试清单(含超通俗生活案例与深度理解)
  • ChatGPT From Zero To Hero - LLM学习笔记(一)
  • 县城网站怎么做英文网页
  • ASP.NET Core 10.0 的主要变化
  • 广州市公司网站建设品牌广州建设网站是什么样的
  • 电感损耗计算方法梳理
  • openEuler - 初探Chrony时间同步服务
  • 【C语言代码】大小写转换
  • 网站开发 程序开发阶段建设公众号官方网站
  • 泰安市建设信息网站哈尔滨通用建设工程有限公司
  • 3dgs项目详解 :convert.py
  • 大模型落地实践指南:从技术路径到企业级解决方案
  • 2004 年真题配套词汇单词笔记(考研真相)
  • Java面经(22届考研-华oD)
  • VS Code 使用 Chrome DevTools MCP 实现浏览器自动化
  • MySQL笔记---内置函数
  • 紫外UV固化太阳光模拟器的原理
  • 南京网站建设哪家好简洁 网站模板
  • react 初体验
  • 南华 NHXJ-02 汽车悬架检验台:技术特性与实操应用指南
  • 鄂湘赣新能源汽车产业质量技术创新联合体成立大会暨汽车网络安全标准专题培训会在汉圆满召开
  • 物联网智能安防系统
  • 洗头竖鞋带名片改良授权做网站不贵高端产品网站建设
  • 软考 系统架构设计师系列知识点之杂项集萃(160)