C++设计模式_16_Adapter 适配器

news2024/12/23 11:07:47

Adapter 适配器也是属于“接口隔离”模式,也是间接思想在某一个层面的应用。

文章目录

  • 1. 动机( Motivation)
  • 2. 模式定义
  • 3. 结构 (Structure)
  • 4. Adapter 适配器的代码实现
  • 5. 要点总结
  • 6. 其他参考

1. 动机( Motivation)

  • 在软件系统中,由于应用环境的变化,常常需要将“一些现存的对象”放在新的环境中应用,但是新环境要求的接口是这些现存对象所不满足的。
  • 如何应对这种“迁移的变化”?如何既能利用现有对象的良好实现,同时又能满足新的应用环境所要求的接口?

适配器模式是非常久远的模式,来源于我们的生活,以下是常见的适配器,适配器的意思对接不同接口的匹配需求
在这里插入图片描述
适配器的思想应用到类的设计中。

2. 模式定义

将一个类的接口转换成客户希望的另一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
----《设计模式》GoF

3. 结构 (Structure)

在这里插入图片描述Target 是未来的也就是希望的接口,Adaptee被适配者是以前的,遗留的接口,我们都希望他们保持不变,但是我们怎么将被适配的接口应用到新的环境,就需要具体的Adapter,Adapter类是继承自Target,我们在学很多模式的时候,常常强调,继承一个类,实际上表明我遵循基类定义的接口规范,在这里也就是Adapter具有Target一样的接口规范。同时可以看到有一个,同时可以看到一个组合,从 Adapter伸出一个箭头指到Adaptee。组合一个类就是支持一个实现的方式, Adapter就实现了把Adaptee往Target的转换。

上图是《设计模式》GoF中定义的Adapter 适配器的设计结构。结合上面的代码看图中最重要的是看其中稳定和变化部分,也就是下图中红框和蓝框框选的部分。
在这里插入图片描述
在具体实现过程中,Adapter 是千变万化的。

4. Adapter 适配器的代码实现

//目标接口(新接口)
class ITarget{
public:
    virtual void process()=0;
};

//遗留接口(老接口)
class IAdaptee{
public:
    virtual void foo(int data)=0;
    virtual int bar()=0;
};

//遗留类型
class OldClass: public IAdaptee{
    //....
};

//对象适配器
class Adapter: public ITarget{ //继承
protected:
    IAdaptee* pAdaptee;//组合
    
public:
    //构造器
    Adapter(IAdaptee* pAdaptee){
        this->pAdaptee=pAdaptee;
    }
    
    virtual void process(){
        int data=pAdaptee->bar();
        pAdaptee->foo(data);
        
    }
    
    
};


//类适配器
class Adapter: public ITarget,
               protected OldClass{ //多继承
               
               
}


int main(){
    IAdaptee* pAdaptee=new OldClass();
    
    
    ITarget* pTarget=new Adapter(pAdaptee);
    pTarget->process();
    
    
}

ITarget是目标接口,IAdaptee是遗留接口,由于某种内在的实现层面的关联性,适配器得能关联,可以把IAdapter转成一个ITarget

Adapter: public ITarget中Adapter继承自ITarget接口,就是遵循其定义的规范。实现virtual void process()=0;,在实现的过程中可能得转换过程如下:

    virtual void process(){
        int data=pAdaptee->bar();
        pAdaptee->foo(data);
        
    }

实际上真正的转换过程在某些平台上是非常复杂的,不像伪码表示的那么简单。第一要存在可转换,第二转换过程是相当复杂的。实际过程中可能类似process()的接口是比较多的,不像此处只有一个,通常可能是其他几个接口联合起来变成其中一个接口。
IAdaptee有一些老的类OldClass是继承自IAdaptee符合老的接口的。
真正在使用时代码如下:

int main(){
    IAdaptee* pAdaptee=new OldClass();    
    
    ITarget* pTarget=new Adapter(pAdaptee);
    pTarget->process();    
}

从此可以拿着一个旧的类 IAdaptee* pAdaptee=new OldClass();塞到Adapter里面ITarget* pTarget=new Adapter(pAdaptee);,当做一个新的类型,面向新接口的实现去使用。

在现实实现过程中,有一些细节可能不一样,比如stl中stack和queue的内部其实都使用了一个deque这样的一个内部的pAdaptee对象,它们都实现了它们的接口转成另外一个接口,那里可能没有明确的感觉谁是新的谁是旧的,只是说利用老的接口deque,转成stack和queue要求的接口,实际上是一个接口转换。那个里面是没有用指针,是直接将deque对象放进去,简单写成伪码就是如下:

class stack{
    deqeue container;
    
};

class queue{
    deqeue container;
    
};

这种情况下,你可能没看到stack的接口是什么,因为stack本身既是接口又是实现,queue也是如此,但其总体符合Adapter设计模式的宗旨,可以将老的接口转成新的接口,所以我们很多时候也把它称为Adapter设计模式。

我们常常讲,大家在学习设计模式过程中,你不能太死板,不一定要追求GoF经典定义一模一样的代码。设计模式会有变体模式,不一定和经典的一致。但是最关键的是这个设计模式的场景,和它解决问题的手法,以及对变化点和稳定点的分离方式是不是符合模式的定义。

5. 要点总结

  • Adapter模式主要应用于“希望复用一些现存的类,但是接口又与复用环境要求不一致的情况”,在遗留代码复用、类库迁移等方面非常有用。
  • GoF23定义了两种Adapter模式的实现结构:对象适配器和类适配器。但类适配器采用“多继承”的实现方式,一般不推荐使用。对象适配器采用“对象组合”的方式,更符合松耦合精神。

上面所讲的是对象适配器,主要区别点是组合了一个对象

//对象适配器
class Adapter: public ITarget{ //继承
protected:
    IAdaptee* pAdaptee;//组合      
};

也有一些采用类适配器,主要区别点是多继承

//类适配器
class Adapter: public ITarget,
               protected OldClass{ //多继承               
}

使用继承OldClass带来的问题就是定死在OldClass,而没有灵活性(继承没有灵活性),而采用组合方式是十分具有灵活性的,IAdaptee* pAdaptee;可以指向OldClass,也可以指向AnotherClass

  • Adapter模式可以实现的非常灵活,不必拘泥于GoF23中定义的两种结构。例如,完全可以将Adapter模式中的“现存对象”作为新的接口方法参数,来达到适配的目的。

6. 其他参考

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1144671.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python:使用Scikit-image对遥感影像进行傅里叶变换特征提取(fourier)

作者:CSDN @ _养乐多_ 在本博客中,我们将介绍如何使用Scikit-Image来进行傅里叶变换特征提取(fourier),并且提供一个示例代码,演示了如何在单波段遥感图像上应用这些方法。 傅里叶变换特征提取是一种数学工具,用于将图像中的细节、纹理和边缘信息以不同频率的方式呈现…

[SpringCloud] Nacos 简介

目录 一、Nacos,启动! 1、安装 Nacos 2、运行 Nacos 3、Nacos 服务注册 二、Nacos 服务多级存储模型 1、服务跨集群分配 2、NacosRule 负载均衡(优先本地) 3、服务实例的权重设置 4、环境隔离 三、Nacos 注册中心细节分…

私有云:【2】AD域的安装

私有云:【2】AD域的安装 1、使用vmwork创建虚拟机2、启动配置虚拟机3、安装域服务4、配置域服务器 1、使用vmwork创建虚拟机 新建虚拟机 稍后安装操作系统 选择win2012,如下图 设置名称及路径 分配硬盘大小,默认60G即可 镜像选择win2012的is…

【数据结构】数组和字符串(九):稀疏矩阵的链接存储:十字链表的插入、查找、删除操作

文章目录 4.2.1 矩阵的数组表示4.2.2 特殊矩阵的压缩存储a. 对角矩阵的压缩存储b~c. 三角、对称矩阵的压缩存储d. 稀疏矩阵的压缩存储——三元组表4.2.3三元组表的转置、加法、乘法、操作4.2.4十字链表0. 十字链表的创建、遍历打印、销毁1. 插入2. 查找3. 删除4. 主函数5. 代码…

C++学习day--22 宏和结构体

1、宏 1.1 为什么要使用宏 1、提高代码的可读性和可维护性 2、避免函数调用,提高程序效率 1.2 什么是宏 它是一种预处理器指令,在预编译阶段将宏名替换为后面的替换体 。 1.3 宏的定义 由三部分组成: #define WIDTH 960 三个部分分别是&…

Python 作用域:局部作用域、全局作用域和使用 global 关键字

变量只在创建它的区域内可用。这被称为作用域。 局部作用域 在函数内部创建的变量属于该函数的局部作用域,并且只能在该函数内部使用。 示例:在函数内部创建的变量在该函数内部可用: def myfunc():x 300print(x)myfunc()函数内部的函数 …

C++——类和对象(中)(2)尚未完结

拷贝构造 概念 在现实生活中,可能存在一个与你一样的自己,我们称其为双胞胎。 那在创建对象时,可否创建一个与已存在对象一某一样的新对象呢? 拷贝构造函数: 只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰)&…

模块化编程

1、函数单独写在一个文件中.c&#xff0c;然后声明也写在一个文件中.h,在mian.c中引用 2、安装目录下面的文件夹用<>,在自己文件夹目录下就是"" 3、创建自己的&#xff08;先把函数放在c文件&#xff0c;再创建头h文件&#xff09;

ruoyi-plus创建模块、自动生成代码

ruoyi-plus自动生成代码 1、创建模块 复制其他部分的resouce过来 修改yml文件 2 修改Nacos 2.1 修改数据库文件 复制其他数据库的链接 &#xff0c;改为自己新建的数据库名字 修改为自己要生成的数据库 新建数据库的yaml文件 3 重启docker的ruoyi-gen服务 docker re…

数据结构【DS】B树

m阶B树的核心特性: Q&#xff1a;根节点的子树数范围是多少&#xff1f;关键字数的范围是多少&#xff1f; A&#xff1a;根节点的子树数∈[2, m],关键字数∈[1, m-1]。 Q&#xff1a;其他结点的子树数范围是多少&#xff1f;关键字数范围是多少&#xff1f; Q&#xff1a;对任…

SSD1306 oled显示屏的驱动SPI接口

有IIC接口 和SPI接口 还有8080,6080接口等 arduino SPI接口 直接使用u8g2库实现 //U8G2_SSD1306_128X64_NONAME_F_4W_SW_SPI u8g2(U8G2_R0, /* clock*/ 13, /* data*/ 11, /* cs*/ 10, /* dc*/ 9, /* reset*/ 8); asrpro(SPI接口按下方修改&#xff0c;IIC接口官方有驱动&…

环形链表(C++解法)

题目 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置&#…

Vmware下的虚拟机NAT连接后仍然木有网络

问题描述 出现在主机能ping通&#xff0c;互联网ping不通的情况。 废话 假设已经设置了网络配置文件IPADDR。 那么&#xff0c;NAT后可以访问互联网的前提是&#xff1a;这个IPADDR的网段在Vmware软件设置的网段内。 解决 在Vmware虚拟网络设置选项卡中&#xff0c;进NAT配…

10000字!图解机器学习特征工程

文章目录 引言特征工程1.特征类型1.1 结构化 vs 非结构化数据1.2 定量 vs 定性数据 2.数据清洗2.1 数据对齐2.2 缺失值处理 原文链接&#xff1a;https://www.showmeai.tech/article-detail/208 作者&#xff1a;showmeAI 引言 上图为大家熟悉的机器学习建模流程图&#xff0c;…

TYWZOJ 礼品配对包装 题解

文章目录 题目描述输入格式输出格式样例样例输入样例输出 数据范围与提示思路与部分实现完整代码 题目描述 爱与愁大神在这家目标店买了 2 x 2x 2x 份礼物&#xff0c;打算分给班级同学。其中有 x x x 份黑礼品&#xff0c; x x x 份白礼品&#xff0c; 2 x 2 2x2 2x2 个空…

计网小题题库整理第一轮(面向期末基础)(3)

基础选择题的最后一章更新&#xff0c;看完期末75至少没问题~ 前情提要&#xff1a; 计网小题题库整理第一轮&#xff08;12期&#xff09; 一.选择题 1、 目前,最流行的以太网组网的拓扑结构是&#xff08; C &#xff09;。 A&#xff09; 总线结构 B&#xff09; 环…

如何能够在发现问题和提问的时候一并带出自己的解决方案

1. 充分理解问题&#xff1a; 在提出问题之前&#xff0c;确保你已经完全理解了问题的本质。从不同的角度分析问题&#xff0c;确保没有遗漏任何重要的信息或者上下文。 2. 进行自我调查和研究&#xff1a; 在向他人寻求帮助之前&#xff0c;尝试自己解决问题。利用网络资源…

Go学习第十三章——Gin(入门与路由)

Go web框架——Gin&#xff08;入门与路由&#xff09; 1 Gin框架介绍1.1 基础介绍1.2 安装Gin1.3 快速使用 2 路由2.1 基本路由GET请求POST请求 2.2 路由参数2.3 路由分组基本分组带中间件的分组 2.4 重定向 1 Gin框架介绍 github链接&#xff1a;https://github.com/gin-gon…

从零开始搭建Prometheus+grafana服务器组件监控系统

服务器及相关组件监控 本文档主要记录了常用企业级服务器及各种组件的监控手段和监控部署方案&#xff0c;使企业可以实时感知服务器组件的健康状态&#xff0c;并在服务器或组件出现异常时及时做出反应。 本方案采用的Prometheusgrafana的方式实现对服务器及各种组件的监控&am…

【前端】NodeJS核心知识点整理

1.Node.js入门案例 1.1.什么是Node.js JS是脚本语言&#xff0c;脚本语言都需要一个解析器才能运行。对于写在HTML页面里的JS&#xff0c;浏览器充当了解析器的角色。而对于需要独立运行的JS&#xff0c;NodeJS就是一个解析器。 每一种解析器都是一个运行环境&#xff0c;不但…