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

适配器模式——不兼容结构的协调

1、简介

1.1、概述

有的笔记本电脑的工作电压是20V,而我国的家庭用电是220V,如何让20V的笔记本电脑能够在220V的电压下工作?答案是引入一个电源适配器(AC Adapter),俗称充电器/变压器。有了这个电源适配器,生活用电和笔记本电脑即可兼容,如下图所示:
在这里插入图片描述

在软件开发中,有时也存在类似这种不兼容的情况,也可以像引入一个电源适配器一样引入一个被称为适配器的角色来协调这些存在不兼容的结构,这种设计方案即为适配器模式。

与电源适配器相似,在适配器模式中引入了一个被称为适配器(Adapter)的包装类,而它所包装的对象称为适配者(Adaptee),即被适配的类。适配器的实现就是把客户类的请求转化为对适配者的相应接口的调用。也就是说:当客户类调用适配器的方法时,在适配器类的内部将调用适配者类的方法,而这个过程对客户类是透明的,客户类并不直接访问适配者类。因此,适配器让那些由于接口不兼容而不能交互的类可以一起工作。

1.2、定义

适配器模式(Adapter Pattern):将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。
:在适配器模式定义中所提及的接口是指广义的接口,它可以表示一个方法或者一组方法的集合。

2、解析

在适配器模式中,通过增加一个新的适配器类来解决接口不兼容的问题,使得原本没有任何关系的类可以协同工作。根据适配器类与适配者类的关系不同,适配器模式可分为对象适配器模式和类适配器模式两种。在对象适配器模式中,适配器与适配者之间是关联关系;

2.1、对象适配模式

在类适配器模式中,适配器与适配者之间是继承(或实现)关系。在实际开发中,对象适配器模式的使用频率更高,其结构如下图所示:

在这里插入图片描述

可以看出,在对象适配器模式结构图中包含以下3个角色:

  • Target(目标抽象类):目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。
  • Adapter(适配器类):适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配。适配器类是适配器模式的核心,在对象适配器模式中,它通过继承Target并关联一个Adaptee对象使二者产生联系。
  • Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配。适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。

代码示例:

/*** 适配者类*/
public class Adaptee {public String specificRequest(String org) {return org;}
}
/*** 适配器类*/
public class Adapter extends Target{// 持有一个对象适配者类对象的引用private Adaptee adaptee;public Adapter(Adaptee adaptee) {this.adaptee = adaptee;}public String request(String org) {// 转发调用return adaptee.specificRequest(org);}
}
/*** 目标抽象类*/
public class Target {public String request(String org) {return org;}}
/*** 客户端*/
public class Client {public static void main(String[] args) {Target target = new Target();target.request("1024");}
}

2.2、类适配器模式

类适配器模式与对象适配器模式最大的区别在于其适配器和适配者之间的关系是继承关系。类适配器模式结构如下图所示。
在这里插入图片描述
所示的类适配器模式结构图,适配器类实现了抽象目标类接口Target,并继承了适配者类。在适配器类的request()方法中调用所继承的适配者类的specificRequest()方法,实现了适配。典型的类适配器模式代码如下:

/*** 适配器类*/
public class Adapter extends Adaptee implements Target {public String request(String org) {// 转发调用return super.specificRequest(org);}
}
/*** 抽象目标类接口*/
public interface Target {String request(String org);}

由于Java、C#等语言不支持多重类继承,因此类适配器模式的使用受到很多限制。例如,如果目标抽象类Target不是接口,而是一个类,就无法使用类适配器模式。此外,如果适配者Adaptee为最终(Final)类,也无法使用类适配器模式。在Java等面向对象编程语言中,大部分情况下使用的是对象适配器模式,类适配器模式较少使用。

2.3、双向适配器模式

在对象适配器模式的使用过程中,如果在适配器中同时包含对目标类和适配者类的引用,适配者可以通过它调用目标类中的方法,目标类也可以通过它调用适配者类中的方法,那么该适配器就是一个双向适配器。其模式结构示意图如下图所示:

在这里插入图片描述

双向适配器模式的实现较为复杂,其典型代码如下:

/*** 适配器类*/
public class Adapter implements Target, Adaptee {// 同时持有抽象目标类和适配者的引用private Target target;private Adaptee adaptee;public Adapter(Target target) {this.target = target;}public Adapter(Adaptee adaptee) {this.adaptee = adaptee;}@Overridepublic String specificRequest(String org) {return adaptee.specificRequest(org);}@Overridepublic String request(String org) {return target.request(org);}
}

2.4、缺省适配器模式

缺省适配器模式是适配器模式的一种变体,其应用也较为广泛。

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

在这里插入图片描述
可以看出,在缺省适配器模式中,包含以下3个角色:

  • ServiceInterface(适配者接口):它是一个接口,通常在该接口中声明了大量的方法。
  • AbstractServiceClass(缺省适配器类):它是缺省适配器模式的核心类,使用空方法的形式实现了在ServiceInterface接口中声明的方法。通常将它定义为抽象类,因为对它进行实例化没有任何意义。
  • ConcreteServiceClass(具体业务类):它是缺省适配器类的子类,在没有引入适配器之前,它需要实现适配者接口,因此需要实现在适配者接口中定义的所有方法,而对于一些无须使用的方法也不得不提供空实现。在有了缺省适配器模式之后,可以直接继承该适配器类,根据需要有选择性地覆盖在适配器类中定义的方法。

3、总结

适配器模式将现有接口转化为客户类所期望的接口,实现了对现有类的复用。它是一种使用频率非常高的设计模式,在软件开发中得以广泛应用,在Spring等开源框架、驱动程序设计(例如JDBC中的数据库驱动程序)中也使用了适配器模式。

3.1、优点

  1. 将目标类和适配者类解耦。通过引入一个适配器类来重用现有的适配者类,无须修改原有结构。
  2. 增加了类的透明性和复用性。将具体的业务实现过程封装在适配者类中,对于客户端类而言是透明的,而且提高了适配者类的复用性,同一个适配者类可以在多个不同的系统中复用。
  3. 灵活性和扩展性都非常好。通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,完全符合开闭原则。具体来说,类适配器模式还有这样的优点:由于适配器类是适配者类的子类,因此可以在适配器类中置换一些适配者的方法,使得适配器的灵活性更强。

对象适配器模式还有如下优点:

(1)一个对象适配器可以把多个不同的适配者适配到同一个目标。

(2)可以适配一个适配者的子类。由于适配器和适配者之间是关联关系,根据里氏代换原则,适配者的子类也可通过该适配器进行适配。

3.2、缺点

  1. 对于Java、C#等不支持多重类继承的语言,一次最多只能适配一个适配者类,不能同时适配多个适配者。
  2. 适配者类不能为最终类,例如在Java中不能为final类,C#中不能为sealed类。
  3. 在Java、C#等语言中,类适配器模式中的目标抽象类只能为接口,不能为类,其使用有一定的局限性。

对象适配器模式的缺点是:与类适配器模式相比,要在适配器中置换适配者类的某些方法比较麻烦。如果一定要置换掉适配者类的一个或多个方法,可以先做一个适配者类的子类,在子类中将适配者类的方法置换掉,然后再把适配者类的子类当作真正的适配者进行适配,实现过程较为复杂。

4、适用场景

  1. 系统需要使用一些现有的类,而这些类的接口(例如方法名)不符合系统的需要,甚至没有这些类的源代码。
  2. 想创建一个可以重复使用的类,用于与一些彼此之间没有太大关联的类,包括一些可能在将来引进的类一起工作。

相关文章:

适配器模式——不兼容结构的协调

1、简介 1.1、概述 有的笔记本电脑的工作电压是20V,而我国的家庭用电是220V,如何让20V的笔记本电脑能够在220V的电压下工作?答案是引入一个电源适配器(AC Adapter),俗称充电器/变压器。有了这…...

【NVIDIA CUDA】2023 CUDA夏令营编程模型(一)

博主未授权任何人或组织机构转载博主任何原创文章,感谢各位对原创的支持! 博主链接 本人就职于国际知名终端厂商,负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作,目前牵头6G算力网络技术标准研究。 博客…...

SHELL——备份脚本

编写脚本,使用mysqldump实现分库分表备份。 1、获取分库备份的库名列表 [rootweb01 scripts]# mysql -uroot -p123456 -e "show databases;" | egrep -v "Database|information_schema|mysql|performance_schema|sys" mysql: [Warning] Using …...

VS创建wsdl服务提供给java调用

文章目录 前言1.c#创建asp.net web服务1.1 创建ASP.NET Web应用程序1.2 添加服务类1.3 定义服务方法1.3 浏览服务1.4 发布服务1.5 IIS部署服务 2.Java中调用服务2.1 用动态客户端工厂类调用2.1.1 引入依赖2.1.2 调用测试代码2.1.3 测试结果 2.2 创建代理类进行调用2.2.1 使用ws…...

盘点 TypeScript 内置类型

盘点 TypeScript 内置类型 盘点 TypeScript 内置类型PartialRequiredReadonlyPickRecordExcludeExtractOmitNonNullableParametersConstructorParametersReturnTypeInstanceTypeUppercaseLowercaseCapitalizeUncapitalize 盘点 TypeScript 内置类型 当开发者开始学习 TypeScri…...

Netty 执行了多次channelReadComplete()却没有执行ChannelRead()

[TOC](Netty 执行了多次channelReadComplete()) Survive by day and develop by night. talk for import biz , show your perfect code,full busy,skip hardness,make a better result,wait for change,challenge Survive. happy for hardess to solve denpendies.…...

直线导轨的精密等级以及划分依据

直线导轨的作用,是用来支撑和引导运动部件,按给定的方向做往复直线运动的,直线导轨是高精密度的传动元件,广泛使用在各行各业中。 直线导轨的精密等级是判断产品质量的一个重要指标。在众多种类的直线导轨产品中,精密等…...

Ubuntu Server版 之 apache系列 常用配置 以及 隐藏 版本号 IP、Port 搭建服务案例

查看版本 旧的 用 httpd -v 新的 用 apache2 -v 配置检测 旧的 httpd -t 新的 apachectl configtest window用的apache 是 httpd -t Linux 中 apachectl configtest 主配置文件 之前旧版apache 是httpd 现在都改成 apache2 /etc/apache2/apache2.conf window中 httpd.con…...

Kubernetes(K8s)从入门到精通系列之七:K8s的基本概念和术语之安全类

Kubernetes K8s从入门到精通系列之七:K8s的基本概念和术语之安全类 一、安全类二、Role和ClusterRole三、RoleBinding和ClusterRoleBinding一、安全类 开发的Pod应用需要通过API Server查询、创建及管理其他相关资源对象,所以这类用户才是K8s的关键用户。K8s设计了Service A…...

网络安全(黑客)自学误区

前言 网络安全是当今社会中至关重要的议题。随着科技的迅猛发展,网络已经渗透到我们生活的方方面面,给我们带来了巨大的便利和机遇。然而,网络也存在着各种风险和威胁,如黑客攻击、数据泄露等。因此,学习网络安全知识…...

在OK3588板卡上部署模型实现人工智能OCR应用

一、主机模型转换 我们依旧采用FastDeploy来部署应用深度学习模型到OK3588板卡上 进入主机Ubuntu的虚拟环境 conda activate ok3588 安装rknn-toolkit2(该工具不能在OK3588板卡上完成模型转换) git clone https://github.com/rockchip-linux/rknn-to…...

在linux中怎样同时运行三个微服务保证退出时不会终止

前言 1.maven中打jar包 使用插件打包,必须在pom.xml中添加插件,否则不能在linux中编译运行 <build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version&g…...

MD-MTSP:成长优化算法GO求解多仓库多旅行商问题MATLAB(可更改数据集,旅行商的数量和起点)

一、成长优化算法GO 成长优化算法&#xff08;Growth Optimizer&#xff0c;GO&#xff09;由Qingke Zhang等人于2023年提出&#xff0c;该算法的设计灵感来源于个人在成长过程中的学习和反思机制。学习是个人通过从外部世界获取知识而成长的过程&#xff0c;反思是检查个体自…...

Python入门一

目录&#xff1a; python基本操作python基本数据类型python字符串基本操作python的运算符python控制流-判断python控制流-循环python常用数据结构-列表python常用数据结构-元组python常用数据结构-集合python常用数据结构-字典python函数python函数进阶与参数处理pythonlambda…...

mysql_2.4——安装常见问题

1. 将MySQL添加到环境变量 将 mysql 的 bin 目录地址添加到 系统环境变量 --> PATH 中 2. 将MySQL添加到服务 以管理员的方式启动 cmd (命令提示窗口)&#xff0c;使用命令进入到 [mysql]\bin &#xff0c;执行如下命 令。 # mysqld --install (服务名) # 如: mysqld --…...

行业追踪,2023-07-31,板块多数都是指向消费

自动复盘 2023-07-31 凡所有相&#xff0c;皆是虚妄。若见诸相非相&#xff0c;即见如来。 k 线图是最好的老师&#xff0c;每天持续发布板块的rps排名&#xff0c;追踪板块&#xff0c;板块来开仓&#xff0c;板块去清仓&#xff0c;丢弃自以为是的想法&#xff0c;板块去留让…...

K8S故障排查

故障现象&#xff1a;部署pod时&#xff0c;报错没发调度到节点。 排查步骤&#xff1a; 1、查看集群的状态 [rootk8s-master1 nginx]#kubectl get nodes2、查看k8s组件的状态-kubelet&#xff0c;kube-apiservice 3、查看docker的Cgroup driver和k8s的Cgroup driver类型&…...

idea集成jrebel实现热部署

文章目录 idea集成jrebel实现热部署下载jrebel 插件包下载jrebel mybatisplus extensition 插件包基础配置信息情况一其次情况三情况四情况五情况六情况七 验证生效与否 Jrebel热部署不生效的解决办法 idea集成jrebel实现热部署 在平常开发项目中&#xff0c;我们通常是修改完…...

【Git系列】Git配置SSH免密登录

&#x1f433;Git配置SSH免密登录 &#x1f9ca;1.设置用户名和邮箱&#x1f9ca;2. 生成密钥&#x1f9ca;3.远程仓库配置密钥&#x1f9ca;2. 免密登录 在以上push操作过程中&#xff0c;我们第一次push时&#xff0c;是需要进行录入用户名和密码的&#xff0c;比较麻烦。而且…...

Node.js 安装与版本管理(nvm 的使用)

安装 Node.js Node.js 诞生于 2009 年 5 月&#xff0c;截至今天&#xff08;2022 年 3 月 26 号&#xff09;的最新版本为 16.14.2 LTS 和 17.8.0 Current&#xff0c;可以去官网下载合适的版本。 其中&#xff0c;LTS&#xff08;Long Term Support&#xff09; 是长期维护…...

[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?

&#x1f9e0; 智能合约中的数据是如何在区块链中保持一致的&#xff1f; 为什么所有区块链节点都能得出相同结果&#xff1f;合约调用这么复杂&#xff0c;状态真能保持一致吗&#xff1f;本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里&#xf…...

基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销&#xff0c;平衡网络负载&#xff0c;延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...

Spring AI 入门:Java 开发者的生成式 AI 实践之路

一、Spring AI 简介 在人工智能技术快速迭代的今天&#xff0c;Spring AI 作为 Spring 生态系统的新生力量&#xff0c;正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务&#xff08;如 OpenAI、Anthropic&#xff09;的无缝对接&…...

Java入门学习详细版(一)

大家好&#xff0c;Java 学习是一个系统学习的过程&#xff0c;核心原则就是“理论 实践 坚持”&#xff0c;并且需循序渐进&#xff0c;不可过于着急&#xff0c;本篇文章推出的这份详细入门学习资料将带大家从零基础开始&#xff0c;逐步掌握 Java 的核心概念和编程技能。 …...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)

骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术&#xff0c;它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton)&#xff1a;由层级结构的骨头组成&#xff0c;类似于人体骨骼蒙皮 (Mesh Skinning)&#xff1a;将模型网格顶点绑定到骨骼上&#xff0c;使骨骼移动…...

GC1808高性能24位立体声音频ADC芯片解析

1. 芯片概述 GC1808是一款24位立体声音频模数转换器&#xff08;ADC&#xff09;&#xff0c;支持8kHz~96kHz采样率&#xff0c;集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器&#xff0c;适用于高保真音频采集场景。 2. 核心特性 高精度&#xff1a;24位分辨率&#xff0c…...

2023赣州旅游投资集团

单选题 1.“不登高山&#xff0c;不知天之高也&#xff1b;不临深溪&#xff0c;不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...

快刀集(1): 一刀斩断视频片头广告

一刀流&#xff1a;用一个简单脚本&#xff0c;秒杀视频片头广告&#xff0c;还你清爽观影体验。 1. 引子 作为一个爱生活、爱学习、爱收藏高清资源的老码农&#xff0c;平时写代码之余看看电影、补补片&#xff0c;是再正常不过的事。 电影嘛&#xff0c;要沉浸&#xff0c;…...

在树莓派上添加音频输入设备的几种方法

在树莓派上添加音频输入设备可以通过以下步骤完成&#xff0c;具体方法取决于设备类型&#xff08;如USB麦克风、3.5mm接口麦克风或HDMI音频输入&#xff09;。以下是详细指南&#xff1a; 1. 连接音频输入设备 USB麦克风/声卡&#xff1a;直接插入树莓派的USB接口。3.5mm麦克…...

论文阅读:LLM4Drive: A Survey of Large Language Models for Autonomous Driving

地址&#xff1a;LLM4Drive: A Survey of Large Language Models for Autonomous Driving 摘要翻译 自动驾驶技术作为推动交通和城市出行变革的催化剂&#xff0c;正从基于规则的系统向数据驱动策略转变。传统的模块化系统受限于级联模块间的累积误差和缺乏灵活性的预设规则。…...