设计模式:适配器模式(Adapter)

设计模式:适配器模式(Adapter)

  • 设计模式:适配器模式(Adapter)
    • 模式动机
    • 模式定义
    • 模式结构
    • 时序图
    • 模式实现
    • 在单线程环境下的测试
    • 在多线程环境下的测试
    • 模式分析
    • 优缺点
    • 适用场景
    • 应用场景
    • 应用实例
    • 适配器模式和代理模式的区别
    • 模式扩展
      • 默认适配器模式(Default Adapter Pattern)
      • 接口适配器模式
    • 参考

设计模式:适配器模式(Adapter)

适配器模式(Adapter)属于结构型模式(Structural Pattern)的一种。

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

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

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

模式动机

通常情况下,客户端可以通过目标类的接口访问它所提供的服务。有时,现有的类可以满足客户类的功能需要,但是它所提供的接口不一定是客户类所期望的,这可能是因为现有类中方法名与目标类中定义的方法名不一致等原因所导致的。

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

模式定义

适配器模式(Adapter)别名为包装器(Wrapper)模式,属于结构型模式。它既可以作为类结构型模式,也可以作为对象结构型模式。

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

模式结构

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

  • 抽象目标(Target):一个抽象类,定义需要适配的标准接口。
  • 适配器类(Adapter):充当中间转换角色,该对象将源对象转换成目标接口。
  • 适配者类(Adaptee) / 源对象(Source):需要被适配的不兼容对象。
  • 客户(Client):通过目标类(Target)的接口访问它所提供的服务。

适配器模式有对象适配器和类适配器两种实现。

对象适配器:

在这里插入图片描述

类适配器:

在这里插入图片描述

时序图

在这里插入图片描述

模式实现

抽象目标 Target.h:

#ifndef _TARGET_H_
#define _TARGET_H_

class Target
{
public:
	virtual void request() = 0;
};

#endif // !_TARGET_H_

适配者类 Adaptee.h:

#ifndef _ADAPTEE_H_
#define _ADAPTEE_H_

#include <iostream>

class Adaptee
{
public:
	void specificRequest()
	{
		std::cout << "Adaptee::specificRequest" << std::endl;
	}
};

#endif // !_ADAPTEE_H_

对象适配器 ObjectAdapter.h:

#ifndef _OBJECT_ADAPTER_H_
#define _OBJECT_ADAPTER_H_

#include "Target.h"
#include "Adaptee.h"

class ObjectAdapter : public Target
{
private:
	Adaptee* m_pAdaptee;

public:
	ObjectAdapter(Adaptee* pAdaptee) :m_pAdaptee(pAdaptee) {};
	virtual ~ObjectAdapter()
	{
		delete m_pAdaptee;
	}

	virtual void request()
	{
		std::cout << "ObjectAdapter::request" << std::endl;
		m_pAdaptee->specificRequest();
	}
};

#endif // !_OBJECT_ADAPTER_H_

类适配器 ClassAdapter.h:

#ifndef _CLASS_ADAPTER_H_
#define _CLASS_ADAPTER_H_

#include "Target.h"
#include "Adaptee.h"

class ClassAdapter : public Target, public Adaptee
{
public:
	virtual void request()
	{
		std::cout << "ClassAdapter::request" << std::endl;
		this->specificRequest();
	}
};

#endif // !_CLASS_ADAPTER_H_

在单线程环境下的测试

测试代码,也可以说是 client:

#include <stdlib.h>

#include "ClassAdapter.h"
#include "ObjectAdapter.h"
#include "Adaptee.h"
#include "Target.h"

using namespace std;

int main(int argc, char* argv[])
{
	Adaptee* adaptee = new Adaptee();
	Target* tar1 = new ClassAdapter();
	Target* tar2 = new ObjectAdapter(adaptee);

	tar1->request();
	tar2->request();
	
	delete adaptee;
	delete tar1;
	delete tar2;

	system("pause");
	return 0;
}

运行结果:

在这里插入图片描述

在多线程环境下的测试

略。

模式分析

  • 对象适配器模式:继承抽象目标类(Target),实现目标接口的所有方法,使用复合的方式,拥有一个适配者类(Adaptee)的对象,调用需要适配对象的方法。
  • 类适配器模式:同时继承抽象目标类(Target)和适配者类(Adaptee),实现目标接口的所有方法,同时继承适配者类的实现,用以完成一些适配逻辑。

优缺点

优点:

  1. 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,而无须修改原有代码。
  2. 增加了类的透明性和复用性,将具体的实现封装在适配者类中,对于客户端类来说是透明的,而且提高了适配者的复用性。
  3. 灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,完全符合“开闭原则”。

类适配器模式还具有如下优点:由于适配器类是适配者类的子类,因此可以在适配器类中置换一些适配者的方法,使得适配器的灵活性更强。

对象适配器模式还具有如下优点:一个对象适配器可以把多个不同的适配者适配到同一个目标,也就是说,同一个适配器可以把适配者类和它的子类都适配到目标接口。

缺点:

  1. 过多的适配器会导致系统结构复杂。
  2. 如果适配器没有实现好,可能会拖慢整个系统的性能。
  3. 滥用适配器模式会导致系统设计紊乱。

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

对象适配器模式的缺点如下:类适配器模式相比,要想置换适配者类的方法就不容易。如果一定要置换掉适配者类的一个或多个方法,就只好先做一个适配者类的子类,将适配者类的方法置换掉,然后再把适配者类的子类当做真正的适配者进行适配,实现过程较为复杂。

适用场景

在以下情况下可以使用适配器模式:

  1. 系统需要使用现有的类,而这些类的接口不符合系统的需要。
  2. 想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。

应用场景

  1. 电压转换器:不同国家的电压规格各异,同样功率的电器在不同的地方工作时需要不同的电压,电压转换器作为适配器,将不同电压转换成电器使用标准电压。
  2. 耳机转接头:有些手机没有耳机插口,需要使用转接头适配器,将耳机转换为手机支持的接口,实现对不同的耳机兼容。
  3. JDBC驱动程序:不同的数据库提供商(如SQL Server、Oracle、MySQL等)实现了不同的JDBC驱动接口,使用适配器模式可以将这些不同的接口适配为标准的JDBC接口,提高应用程序的可移植性。
  4. 日志框架:Java中有多个常用的日志框架,如Log4j、SLF4J等,不同的日志框架提供的API不同,使用适配器模式可以将这些不同的API适配为一个统一的接口,方便再程序中进行日志记录和管理。
  5. 第三方库或SDK:在使用第三方库或 SDK 时,可能由于它们实现的 API 不同而导致应用程序复杂,使用适配器模式可以将不同的 API 适配为统一的接口,简化应用程序的调用。

应用实例

当我们去国外旅游时,我们可能只会汉语,而当地人只会英语,那么这个时候就需要一个翻译员(翻译软件)来帮助我们。这就类似于适配器模式,通过一个适配器将一个不兼容的接口转成另外一个接口。下面以翻译为例,介绍一下类、接口、对象适配器。

  • 目标接口 Target:里面定义了一个 translate 方法。
  • 翻译类 Translator:就是适配者类,里面有 TranslateInZh 和 TranslateInEn 方法。
  • 对象适配器 ObjectAdapter 和类适配器 ClassAdapter。

对象适配器模式:

在这里插入图片描述

类适配器模式:

在这里插入图片描述

完整程序:

#include <iostream>
#include <stdlib.h>
using namespace std;

class Target
{
public:
	virtual void translate(string source, string target, string words) = 0;
};

class Translator
{
public:
	void translateInZh(string words)
	{
		if (words == "hello world!")
			cout << "你好世界!" << endl;
	}
	void translateInEn(string words)
	{
		if (words == "你好世界!")
			cout << "Translate in English: \"hello world!\"" << endl;
	}
};

class ClassAdapter : public Target, public Translator
{
public:
	virtual void translate(string source, string target, string words)
	{
		if (source == "中文" && target == "英文")
			this->translateInEn(words);
		else
			this->translateInZh(words);
	}
};

class ObjectAdapter : public Target
{
private:
	Translator *m_pTranslator;

public:
	ObjectAdapter(Translator *trans) : m_pTranslator(trans){};
	virtual ~ObjectAdapter()
	{
		delete m_pTranslator;
	}

	virtual void translate(string source, string target, string words)
	{
		if (source == "中文" && target == "英文")
			m_pTranslator->translateInEn(words);
		else
			m_pTranslator->translateInZh(words);
	}
};

int main()
{
	Translator* translator = new Translator();
	Target *tar1 = new ClassAdapter();
	Target *tar2 = new ObjectAdapter(translator);
	
	tar1->translate("中文", "英文", "你好世界!");
	tar1->translate("英文", "中文", "hello world!");
	cout << endl;
	tar2->translate("中文", "英文", "你好世界!");
	tar2->translate("英文", "中文", "hello world!");

	system("pause");
	return 0;
}

运行结果:

在这里插入图片描述

适配器模式和代理模式的区别

适配器模式的主要目的是使接口不兼容的对象能够协同工作。适配器模式允许将⼀个类的接口转换成另⼀个类的接口,使得不同接⼝的类可以协同工作。

代理模式的主要目的是控制对对象的访问。通常⽤于在访问真实对象时引入⼀些额外的控制逻辑,如权限控制、延迟加载等。

模式扩展

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

也称为缺省适配器模式。

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

接口适配器模式

主要适用于需要被适配的接口中,只有用到个别接口,也就是说不需要实现它的全部接口。通过一个中间抽象类或接口实现。

在这里插入图片描述

参考

  1. https://design-patterns.readthedocs.io/zh-cn/latest/structural_patterns/adapter.html#
  2. https://www.runoob.com/design-pattern/adapter-pattern.html
  3. https://blog.csdn.net/weixin_45433817/article/details/131037102

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

JAVA实现图书管理系统(初阶)

一.抽象出对象: 1.要有书架&#xff0c;图书&#xff0c;用户&#xff08;包括普通用户&#xff0c;管理员用户&#xff09;。根据这些我们可以建立几个包&#xff0c;来把繁杂的代码分开&#xff0c;再通过一个类来把这些&#xff0c;对象整合起来实现系统。说到整合&#xf…

Day04:CSS 进阶

目标&#xff1a;掌握复合选择器作用和写法&#xff1b;使用background属性添加背景效果 一、复合选择器 定义&#xff1a;由两个或多个基础选择器&#xff0c;通过不同的方式组合而成。 作用&#xff1a;更准确、更高效的选择目标元素&#xff08;标签&#xff09;。 1、后…

ros 学习记录(四)仿真环境中键盘控制turtlebot3运动

仿真环境中键盘控制turtlebot3运动 准备工作1. 监听键盘敲击&#xff1a;key_publisher.py2. 控制turtlebot3运动&#xff1a;keys_to_twist_using_rate.py3. 测试4. 数据监视 准备工作 名称版本ROSNoeticGazebo11.11.0python3.8.10 turtlebot3的安装与仿真环境搭建请看上一篇…

视频播放器-Kodi

一、前言 Kodi 是一款开源免费的多媒体播放软件。Kodi 是由非营利性技术联盟 Kodi 基金会开发的免费开源媒体播放器应用程序。 Kodi是一款免费和开源&#xff08;遵循GPL协议&#xff09;的多媒体播放器和娱乐中心软件&#xff0c;由XBMC基金会开发。Kodi的主要功能是管理和播…

three.js判断物体在人的前面,还是后面

three.js判断物体在人的前面&#xff0c;还是后面 const player new THREE.Vectors(10, 0, 5); const mesh new THREE.Vectors(15, 0, 6);上面&#xff0c;两个变量分别表示&#xff0c;玩家的位置&#xff0c;物体的位置。 从这发现&#xff0c;当玩家和物体的角度关系 小…

报错:c2665 ”loadimage“没有重载函数可以转换所有参数类型

解决方法: 右键项目选择属性-》高级-》字符集-》使用多字节字符集-》确定

SparkStreaming概述

Spark概述 SparkStreaming概述 Spark Streaming 是 Apache Spark 生态系统中的一个组件&#xff0c;用于实时流数据处理。它允许用户通过流式计算引擎处理实时数据流&#xff0c;并以低延迟的方式对数据进行分析、处理和存储。 背景 在大数据领域&#xff0c;传统的批处理系统…

什么是云渗透测试?

推荐阅读&#xff1a; 什么是安全态势&#xff1f; 什么是人肉搜索 什么是恶意软件&#xff1f; 什么是数字取证&#xff1f; 什么是语音网络钓鱼&#xff1f; 什么是网络安全中的社会工程&#xff1f; 什么是网络安全中的威胁情报&#xff1f; 什么是端点检测和响应 (…

Java进阶:详解与实战Java Stream API

Java进阶&#xff1a;详解与实战Java Stream API &#x1f31f; Java进阶&#xff1a;详解与实战Java Stream API &#x1f31f;摘要引言一、Java Stream API介绍&#x1f4da;1. 什么是Java Stream API&#xff1f;2. Java Stream API支持的功能3. 使用Java Stream API的优势…

【vue3+elementuiplus】el-select下拉框会自动触发校验规则

场景&#xff1a;编辑弹框省份字段下拉框必填&#xff0c;触发方式change&#xff0c;有值第一次打开不会触发校验提示&#xff0c;关闭弹框再次打开触发必填校验提示&#xff0c;但是该字段有值 问题的原因是&#xff1a;在关闭弹层事件中&#xff0c;我做了resetfileds&…

react-d3-tree:React组件创建交互式D3树形图

在这里插入代码片import React from "react"; import ReactDOM from "react-dom"; import Tree from "react-d3-tree";import "./styles.css";const myTreeData [{name: "Gaurang Torvekar",attributes: {keyA: "val …

如何使用KNN

导入文件和库 加载数据集、拆分数据集 训练模型 预测 打印结果

自定义Linux命令,显示docker镜像、容器信息

1、修改环境变量&#xff08;仅对当前用户有效&#xff09; vim ~/.bashrc2、给命令取别名 alias dpsdocker ps --format "table{{.ID}}\t{{.Names}}\t{{.Status}}\t{{.Ports}}" alias disdocker images#保存并退出 :wq3、让配置重新生效 source ~/.bashrc4、测试&…

STM32F1之OV7725摄像头·SCCB总线代码编写附带源码详解

STM32F1之OV7725摄像头-CSDN博客 STM32F1之OV7725摄像头像素数据输出时序、FIFO 读写时序以及摄像头的驱动原理详解-CSDN博客 目录 1. 硬件设计 1.1 SCCB 控制相关 1.2 VGA 时序相关 1.3 FIFO 相关 1.4 XCLK 信号 2. 代码设计 2.1 SCCB总线软件实现 2.1.1 宏定…

【GUI开发基础】

GUI开发基础 &#x1f31f;项目文件组成✨浅析Pro文件配置 &#x1f31f;Qt设计师&#x1f31f;剖析UI文件运行机制&#x1f31f;UI设计方式✨可视化UI设计✨代码化UI设计 &#x1f31f;项目文件组成 创建一个QtGUI项目&#xff1a; open QtCreator —> select Creator Pr…

Nvidia 如何成为 AI 训练的超级强国

周三&#xff0c;英伟达公布了第一季度的财务业绩&#xff0c;再次超出了分析师的预期。在截至 4 月 28 日的季度中&#xff0c;该公司的利润同比飙升 262%&#xff0c;股价一度创下 1000 美元以上的新高。 目前&#xff0c;英伟达的市值超过 2.3 万亿美元&#xff0c;是全球第…

从0开始学统计-什么是相关?

1.什么是统计学相关? 在统计学中&#xff0c;“相关”&#xff08;Correlation&#xff09;是指两个变量之间的线性关系程度。相关关系可以表明两个变量在某种程度上共同变化的趋势&#xff0c;但不意味着因果关系。相关的主要衡量方法是相关系数&#xff08;Correlation Coe…

【Linux-INPUT输入的子系统】

Linux-INPUT输入的子系统 ■ input 子系统简介■ input 驱动编写流程■ ■ input 子系统简介 input 子系统就是管理输入的子系统&#xff0c; input 子系统分为 input 驱动层、 input 核心层、 input 事件处理层&#xff0c;最终给用户空间提供可访问的设备节点 ■ input 驱…

shell脚本实战--批量修改文件名

字符串截取 先来了解一下shell字符串相关操作的变量 # 从开头删除匹配最短 ## 从开头删除匹配最长 % 从结尾削除匹配最短 %% 从结尾删除匹配最长#指定字符内容截取 a*c 匹配开头为a&#xff0c;中间任意个字符&#xff0c;结尾为c的字符串 a*C 匹配…

web学习笔记(五十八)

目录 1. v-model 双向数据绑定 2. 事件修饰符 3. 路径别名 4. setup语法糖 4.1 语法糖的概念 4.2 setup语法糖 5. 配置代理服务器 1. v-model 双向数据绑定 v-model 双向数据绑定只能使用在表单标签&#xff1b; v-model双向数据绑定原理&#xff1a;采用 Object.de…