适配器模式——不兼容结构的协调
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.2、缺点
- 对于Java、C#等不支持多重类继承的语言,一次最多只能适配一个适配者类,不能同时适配多个适配者。
- 适配者类不能为最终类,例如在Java中不能为final类,C#中不能为sealed类。
- 在Java、C#等语言中,类适配器模式中的目标抽象类只能为接口,不能为类,其使用有一定的局限性。
对象适配器模式的缺点是:与类适配器模式相比,要在适配器中置换适配者类的某些方法比较麻烦。如果一定要置换掉适配者类的一个或多个方法,可以先做一个适配者类的子类,在子类中将适配者类的方法置换掉,然后再把适配者类的子类当作真正的适配者进行适配,实现过程较为复杂。
4、适用场景
- 系统需要使用一些现有的类,而这些类的接口(例如方法名)不符合系统的需要,甚至没有这些类的源代码。
- 想创建一个可以重复使用的类,用于与一些彼此之间没有太大关联的类,包括一些可能在将来引进的类一起工作。
相关文章:

适配器模式——不兼容结构的协调
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 成长优化算法(Growth Optimizer,GO)由Qingke Zhang等人于2023年提出,该算法的设计灵感来源于个人在成长过程中的学习和反思机制。学习是个人通过从外部世界获取知识而成长的过程,反思是检查个体自…...

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

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

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

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

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

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

Node.js 安装与版本管理(nvm 的使用)
安装 Node.js Node.js 诞生于 2009 年 5 月,截至今天(2022 年 3 月 26 号)的最新版本为 16.14.2 LTS 和 17.8.0 Current,可以去官网下载合适的版本。 其中,LTS(Long Term Support) 是长期维护…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度
一、引言:多云环境的技术复杂性本质 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时,基础设施的技术债呈现指数级积累。网络连接、身份认证、成本管理这三大核心挑战相互嵌套:跨云网络构建数据…...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...
Java 8 Stream API 入门到实践详解
一、告别 for 循环! 传统痛点: Java 8 之前,集合操作离不开冗长的 for 循环和匿名类。例如,过滤列表中的偶数: List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...

全球首个30米分辨率湿地数据集(2000—2022)
数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...
.Net Framework 4/C# 关键字(非常用,持续更新...)
一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...
laravel8+vue3.0+element-plus搭建方法
创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...
Java 二维码
Java 二维码 **技术:**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...