C++设计模式之适配器模式(adapter)(结构型)

2023-10-26

一、结构型模式概述

    结构型模式(Structural Pattern)描述如何将类或者对象结合在一起形成更大的结构,就像搭积木,可以通过简单积木的组合形成复杂的、功能更为强大的结构。

    结构型模式可以分为类结构型模式对象结构型模式

        • 类结构型模式关心类的组合,由多个类可以组合成一个更大的系统,在类结构型模式中一般只存在继承关系和实现关系。

        • 对象结构型模式关心类与对象的组合,通过关联关系使得在一个类中定义另一个类的实例对象,然后通过该对象调用其方法。根据“合成复用原则”,在系统中尽量使用关联关系来替代继承关系,因此大部分结构型模式都是对象结构型模式。

类适配器和对象适配器区别

类适配器的重点在于类,是通过构造一个继承Adaptee类来实现适配器的功能;
对象适配器的重点在于对象,是通过在直接包含Adaptee类来实现的,当需要调用特殊功能的时候直接使用Adapter中包含的那个Adaptee对象来调用特殊功能的方法即可。

注意事项:

类适配器使用多重继承对一个接口与另一个接口进行匹配。对象适配器依赖于对象组合。类适配器和对象适配器有不同的权衡。类适配器:A、用一个具体的Adapter类对Adaptee和Target进行匹配。结果是当我们想要匹配一个类以及所有它的子类时,类Adapter将不能胜任工作;B、使得Adapter可以重新定义Adaptee的部分行为,因为Adapter是Adaptee的一个子类;C、仅仅引入了一个对象,并不需要额外的指针以间接得到adaptee。

对象适配器:A、允许一个Adapter与多个Adaptee----即Adaptee本身以及它的所有子类(如果有子类的话)同时工作。Adapter也可以一次给所有的Adaptee添加功能;B、使得重定义Adaptee的行为比较困难。这就需要生成Adaptee的子类并且使得Adapter引用这个子类而不是引用Adaptee本身。

(使用Adapter模式时需要考虑的其它一些因素有:A、Adapter的匹配程度:对Adaptee的接口与Target的接口进行匹配的工作量各个Adapter可能不一样。工作范围可能是,从简单的接口转换到支持完全不同的操作集合。Adapter的工作量取决于Target接口与Adaptee接口的相似程度。B、可插入的Adapter:当其它的类使用一个类时,如果所需的假定条件越少,这个类就更具可复用性。如果将接口匹配构建为一个类,就不需要假定对其它的类可见的是一个相同的接口。也就是说,接口匹配使得我们可以将自己的类加入到一些现有的系统中去,而这些系统对这个类的接口可能会有所不同。
 

二、模式定义

适配器模式将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。它包括类适配器和对象适配器。

在软件开发中采用类似于电源适配器的设计和编码技巧被称为适配器模式。

    通常情况下,客户端可以通过目标类的接口访问它所提供的服务。有时,现有的类可以满足客户类的功能需要,但是它所提供的接口不一定是客户类所期望的,这可能是因为现有类中方法名与目标类中定义的方法名不一致等原因所导致的。在这种情况下,现有的接口需要转化为客户类期望的接口,这样保证了对现有类的重用。如果不进行这样的转化,客户类就不能利用现有类所提供的功能,适配器模式可以完成这样的转化。

    在适配器模式中可以定义一个包装类,包装不兼容接口的对象,这个包装类指的就是适配器(Adapter),它所包装的对象就是适配者(Adaptee),即被适配的类。

    适配器提供客户类需要的接口,适配器的实现就是把客户类的请求转化为对适配者的相应接口的调用。也就是说:当客户类调用适配器的方法时,在适配器类的内部将调用适配者类的方法,而这个过程对客户类是透明的,客户类并不直接访问适配者类。因此,适配器可以使由于接口不兼容而不能交互的类可以一起工作。这就是适配器模式的模式动机。

 

三、ULM图

类适配器

 

 对象适配器

适配器模式包含如下角色:

    • Target:目标抽象类

    • Adapter:适配器类

    • Adaptee:适配者类

    • Client:客户类

在这里,Target这个类中的接口才是客户所期望的类。Adapter在适配器内部包装了一个被是陪对象既Adaptee,把源接口转换为目标接口。Adaptee类是需要适配的类,其中的接口并不是Client所期望的接口所以需要被适配。

对象适配器则
• 允许一个Adapter与多个Adaptee—即Adaptee本身以及它的所有子类(如果有子类的话)—同时工作。Adapter也可以一次给所有的Adaptee添加功能。
• 使得重定义Adaptee的行为比较困难。这就需要生成Adaptee的子类并且使得Adapter引用这个子类而不是引用Adaptee本身。
使用Adapter模式时需要考虑的其他一些因素有:

1) Adapter的匹配程度 对Adaptee的接口与Target的接口进行匹配的工作量各个Adapter可能不一样。工作范围可能是,从简单的接口转换(例如改变操作名 )到支持完全不同的操作集合。Adapter的工作量取决于Target接口与Adaptee接口的相似程度
2) 可插入的Adapter   当其他的类使用一个类时,如果所需的假定条件越少,这个类就更具可复用性。如果将接口匹配构建为一个类,
就不需要假定对其他的类可见的是一个相同的接口。也就是说,接口匹配使得我们可以将自己的类加入到一些现有的系统中去,
而这些系统对这个类的接口可能会有所不同。 
3) 使用双向适配器提供透明操作 使用适配器的一个潜在问题是,它们不对所有的客户都透明。被适配的对象不再兼容 Adaptee的接口,
因此并不是所有 Adaptee对象可以被使用的地方它都可以被使用。双向适配器提供了这样的透明性。
在两个不同的客户需要用不同的方式查看同一个对象时,双向适配器尤其有用。

四、实例

4.1 STL中queue和stack(对象适配器)

在STL中就用到了适配器模式。STL实现了一种数据结构,称为双端队列(deque),支持前后两段的插入与删除。STL实现栈和队列时,没有从头开始定义它们,而是直接使用双端队列实现的。这里双端队列就扮演了适配器的角色。队列用到了它的后端插入,前端删除。而栈用到了它的后端插入,后端删除。假设栈和队列都是一种顺序容器,有两种操作:压入和弹出。

#include <iostream>

//双端队列(已有实现的类和接口)
class Deque
{
public:
	void push_back(int x) { std::cout << "Deque push_back" << std::endl; }
	void push_front(int x) { std::cout << "Deque push_front" << std::endl; }
	void pop_back() { std::cout << "Deque pop_back" << std::endl; }
	void pop_front() { std::cout << "Deque pop_front" << std::endl; }
};
//顺序容器(客户端期望的类和接口)
class Sequence
{
public:
	virtual void push(int x) = 0;
	virtual void pop() = 0;
};
//栈(adapter)
class Stack : public Sequence
{
public:
	void push(int x) { deque.push_back(x); }
	void pop() { deque.pop_back(); }
private:
	Deque deque; //双端队列
};
//队列(adapter)
class Queue : public Sequence
{
public:
	void push(int x) { deque.push_back(x); }
	void pop() { deque.pop_front(); }
private:
	Deque deque; //双端队列
};

int main()
{
	Sequence* s1 = new Stack();//客户期望的stack
	Sequence* s2 = new Queue();//客户期望的queue

	s1->push(1); 
	s1->pop();

	s2->push(1); 
	s2->pop();

	delete s1; 
	delete s2;

	return 0;
}

4.2 仿生机器人(类适配器)

现需要设计一个可以模拟各种动物行为的机器人,在机器人中定义了一系列方法,如机器人叫喊方法cry()、机器人移动方法move()等。如果希望在不修改已有代码的基础上使得机器人能够像狗一样叫,像狗一样跑,使用适配器模式进行系统设计。这里只需要通过cry适配wang,通过move适配run。

#include <iostream>

//target抽象类Robot
class Robot {
public:
	virtual void cry() = 0;
	virtual void move() = 0;
};

//adaptee类Dog
class Dog
{
public:
	void wang() 
	{
		std::cout << "狗汪汪叫!" << std::endl;
	}

	void run() 
	{
		std::cout << "狗快快跑!" << std::endl;
	}
};

//adapter类DogAdapter
class DogAdapter : public Robot, private Dog 
{
public:
	void cry() 
	{
		std::cout << "机器人模仿:" << std::endl;
		Dog::wang();
	}

	void move() 
	{
		std::cout << "机器人模仿:" << std::endl;
		Dog::run();
	}
};

//Client 
int main(void) 
{
	Robot* robot = (Robot*)new DogAdapter();
	robot->cry();
	robot->move();
	return 0;
}

结果如下:

五、模式优缺点

优点

    • 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,而无须修改原有代码。

    • 增加了类的透明性和复用性,将具体的实现封装在适配者类中,对于客户端类来说是透明的,而且提高了适配者的复用性。

    • 灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,完全符合“开闭原则”。

    • 由于适配器类是适配者类的子类,因此可以在适配器类中置换一些适配者的方法,使得适配器的灵活性更强。

缺点

    • 对于Java、C#等不支持多重继承的语言,一次最多只能适配一个适配者类,而且目标抽象类只能为抽象类,不能为具体类,其使用有一定的局限性,不能将一个适配者类和它的子类都适配到目标接口。

六、模式扩展

默认适配器模式(Default Adapter Pattern)或缺省适配器模式

     • 当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求,它适用于一个接口不想使用其所有的方法的情况。因此也称为单接口适配器模式。

双向适配器

    • 在对象适配器的使用过程中,如果在适配器中同时包含对目标类和适配者类的引用,适配者可以通过它调用目标类中的方法,目标类也可以通过它调用适配者类中的方法,那么该适配器就是一个双向适配器。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++设计模式之适配器模式(adapter)(结构型) 的相关文章

随机推荐

  • SqliLabs Less25-25a

    第二十五关 GET类型基于单引号 and or过滤 你所有的and or都是我们的 的错误 1 使用union select 1 2 3 尽量避过and or http 127 0 0 1 500 Less 25 id 1 union se
  • 机器学习笔记-多分类学习,类别不平衡,决策树

    读书笔记 多分类学习 基本思想 拆解法 将多分类任务拆解为若干个二分类任务求解 先对这些问题经拆分 为拆分出的每个二分类任务训练一个分类器 测试时 对这些分类器的预测结果进行集成以获得最终的多分类结果 拆分策略 one vs one 一对一
  • 【教程】TestComplete测试桌面应用程序教程(六)

    TestComplete是一款具有人工智能的自动UI测试工具 利用自动化测试工具和人工智能支持的混合对象识别引擎 轻松检测和测试每个桌面 Web和移动应用程序 其中 TestComplete支持测试使用C C VB NET Java Del
  • 【STM32】STM32单片机结构及部件原理

    STM32是目前比较常见并且多功能的单片机 要想学习STM32 首先要去了解它的基本构成部分以及各部分的原理 单片机型号 正点原子STM32F103ZET6 目录 STM32内部结构总览图 2 内部结构解析 STM32内部结构总览图 2 内
  • 网络正常,某个网页或软件无法加载

    你是否经历过切换网络后 有时会出现虽然能够上网 但偶尔出现个别网页加载失败 知乎 哔哩哔哩 一般是由于设备DNS缓存出现异常 以下是针对个别系统的解决方法 其他系统原理相同 win10 1 进入网络适配器 更改Internet协议DNS服务
  • PCL RANSAC 拟合直线

    RANSAC拟合直线 一 算法原理 1 算法简介 2 直线拟合 3 模型系数 4 参考文献 二 代码实现 三 结果展示 四 python代码 一 算法原理 1 算法简介 RANSAC算法由Fischler和Bolles于1981年提出 是一
  • linux下mysql写中文变成问号_如何解决数据库插入中文字体时显示问号

    欢迎点击 算法与编程之美 关注我们 本文首发于微信公众号 算法与编程之美 欢迎关注 及时了解更多此系列文章 问题描述 我们在进行数据库的增删改查的操作时 当我们插入英文或者数字等字符串的时候能够正常显示 但的当我们插入中文字体的时候我们就会
  • 火狐解决OCSP回应包含过期信息的问题

    连接 addons mozilla org 时发生错误 OCSP 回应包含过期信息 错误码 sec error ocsp old response hosts文件添加 vi etc hosts 117 18 237 29 ocsp digi
  • 马云的一席话

    关于坚持 今天很残酷 明天更残酷 后天很美好 但是大多数人死在明天晚上 看不到后天的太阳 所谓坚持成功 不是坚持 一直成功 而是坚持到 成功为止 关于创新 做任何事 必须要有突破 没有突破 就等于没做 行业饱和即意味危机来临 但巴菲特在股票
  • Java中JSON数据的读取和解析

    在做springboot项目时用到了json文件读取和解析 所以在这里记录一下学习过程中总结的一些点 希望对大家有帮助 配置fastJson
  • 超高清

    海思 HDR HDR行业面临巨大挑战 01 标准不统一 终端呈现效果参差不齐 HDR多种技术标准共存 缺少终端侧技术实现方案 标准间兼容性较差 不能覆盖主流终端的适配 认证及测试过程 导致终端呈现效果差距大 02 生态碎片化 部分技术方案专
  • Cordova 环境搭建+打包Android APK

    一 环境搭建 1 JDK 1 1 下载JDK http www oracle com technetwork java javase downloads jdk8 downloads 2133151 html 在上面的网址中选择符合自己操作
  • 正则表达式匹配案例

    匹配案例 1 判断变量的名称 由数字 字母 下划线组成 不能以数字作为开头 a zA Z a zA Z0 9 注意 如果不加 中间如果有不符合字符如abc 123就会匹配成功 import re result re match r a zA
  • 《ReactNative系列讲义》进阶篇---06.FlatList(三)

    版权声明 本文为博主原创文章 未经博主允许不得转载 一 简介 截止到上篇文章 关于FlatList无论是简单的还是高级的属性用法都已经介绍完毕 今天我们一起来看看FlatList更高级的玩法 相关方法的调用 二 基础知识 获取FlatLis
  • Elasticsearch的关键词搜索

    返回给前端的实体类 Data AllArgsConstructor NoArgsConstructor public class PageResult private Long total private List
  • el-table纵向垂直表头

    参考 https www jianshu com p 1f38eaffd070
  • facenet采坑之旅,主要记录一些用facenet过程中遇到的大大小小的问题

    问题1 Unable to run align dataset mtcnn py getting an attribute error module facenet has no attribute store revision info
  • 封装一个带el-form的,带el-table的,带分页的,带搜索查询的dialog组件,很使用的二次封装组件。

    封装dialog小案例 提示 这是我工作中封装的代码 很使用 需要的可以拿去 在我们的代码中往往会出现点击按钮出现弹窗进行操作 那么我们就需要对dialog进行一个二次封装 下边是大概的一个样式 对组件进行二次封装
  • burpsuite与sqlmap结合使用之CO2

    在使用sqlmap时 对一个页面进行注入时需要认证信息 如果将页面信息每次都保存到本地比较麻烦 使用命令行指令也需要cookie值 burpsuite中有一个快速sqlmap扫描的工具 CO2是一个burp插件 burp将拦截的请求直接发给
  • C++设计模式之适配器模式(adapter)(结构型)

    一 结构型模式概述 结构型模式 Structural Pattern 描述如何将类或者对象结合在一起形成更大的结构 就像搭积木 可以通过简单积木的组合形成复杂的 功能更为强大的结构 结构型模式可以分为类结构型模式和对象结构型模式 类结构型模