二十三种设计模式-适配器模式
适配器模式(Adapter Pattern)是一种结构型设计模式,它允许将不兼容的接口转换成客户端期望的接口,从而使原本因接口不匹配而不能一起工作的类可以协同工作。以下是关于适配器模式的详细介绍:
一、定义及作用
- 定义:适配器模式将一个类的接口转换成客户端所期望的另一种接口,使原本由于接口不兼容而不能一起工作的那些类可以一起工作。
- 作用:
- 兼容性:解决了接口不兼容的问题,让原本无法协同工作的类能够一起工作,提高了系统的兼容性。
- 复用性:可以重用一些现有的类,而无需修改它们的代码,从而提高了代码的复用性。
- 灵活性:客户端调用的是适配器类,而适配器类可以根据需要调用被适配类的方法,这样可以在不修改客户端代码的情况下,灵活地更换被适配类。
二、实现方式
适配器模式主要有两种实现方式:类适配器和对象适配器。
(一)类适配器
- 结构:
- 目标接口(Target):定义客户端所期望的接口,它声明了一些方法,客户端通过这些方法来调用适配器的功能。
- 适配器类(Adapter):通过继承被适配类来实现目标接口。它将被适配类的接口转换为目标接口,内部持有被适配类的实例,并将目标接口的方法调用转发给被适配类的实例。
- 被适配类(Adaptee):定义了需要适配的接口,它有一些有用的方法,但是接口与客户端期望的接口不一致。
- 示例:
- 假设有一个旧的音频播放器类
AudioPlayer
,它只能播放MP3格式的音频文件,接口方法为playMp3(String fileName)
。现在客户端希望它也能播放VLC格式的音频文件。 - 我们可以创建一个适配器类
AudioPlayerAdapter
,它继承自AudioPlayer
,同时实现一个新的接口AdvancedAudioPlayer
,该接口有一个方法play(String audioType, String fileName)
。 - 在
AudioPlayerAdapter
类中,重写play
方法,当audioType
为“vlc”时,先将VLC格式的音频文件转换为MP3格式(假设有一个转换方法convertVlcToMp3
),然后再调用playMp3
方法来播放转换后的MP3文件。
- 假设有一个旧的音频播放器类
// 目标接口
interface AdvancedAudioPlayer {void play(String audioType, String fileName);
}// 被适配类
class AudioPlayer {public void playMp3(String fileName) {System.out.println("Playing mp3 file. Name: " + fileName);}
}// 适配器类
class AudioPlayerAdapter extends AudioPlayer implements AdvancedAudioPlayer {@Overridepublic void play(String audioType, String fileName) {if (audioType.equalsIgnoreCase("vlc")) {String convertedFile = convertVlcToMp3(fileName);playMp3(convertedFile);}}private String convertVlcToMp3(String fileName) {// 转换逻辑,这里简化处理return fileName.replace(".vlc", ".mp3");}
}
(二)对象适配器
- 结构:
- 目标接口(Target):与类适配器中的目标接口相同,定义客户端期望的接口。
- 适配器类(Adapter):实现目标接口,并且通过组合的方式包含一个被适配类的实例。适配器类将目标接口的方法调用转发给被适配类的实例。
- 被适配类(Adaptee):与类适配器中的被适配类相同,定义了需要适配的接口。
- 示例:
- 还是以上面的音频播放器为例。适配器类
AudioPlayerAdapter
实现AdvancedAudioPlayer
接口,同时在类中包含一个AudioPlayer
类型的成员变量。 - 在
AudioPlayerAdapter
的构造方法中,传入一个AudioPlayer
实例。在play
方法中,根据audioType
判断,如果是VLC格式,先调用转换方法,然后通过成员变量调用AudioPlayer
的playMp3
方法。
- 还是以上面的音频播放器为例。适配器类
// 目标接口
interface AdvancedAudioPlayer {void play(String audioType, String fileName);
}// 被适配类
class AudioPlayer {public void playMp3(String fileName) {System.out.println("Playing mp3 file. Name: " + fileName);}
}// 适配器类
class AudioPlayerAdapter implements AdvancedAudioPlayer {private AudioPlayer audioPlayer;public AudioPlayerAdapter(AudioPlayer audioPlayer) {this.audioPlayer = audioPlayer;}@Overridepublic void play(String audioType, String fileName) {if (audioType.equalsIgnoreCase("vlc")) {String convertedFile = convertVlcToMp3(fileName);audioPlayer.playMp3(convertedFile);}}private String convertVlcToMp3(String fileName) {// 转换逻辑,这里简化处理return fileName.replace(".vlc", ".mp3");}
}
三、优缺点
- 优点:
- 提高兼容性:可以将不兼容的接口转换为目标接口,让原本不能一起工作的类能够协同工作。
- 提高复用性:可以重用一些现有的类,无需修改它们的代码,提高了代码的复用性。
- 符合开闭原则:客户端调用的是适配器类,而适配器类可以根据需要调用被适配类的方法,可以在不修改客户端代码的情况下,灵活地更换被适配类。
- 缺点:
- 类适配器的缺点:由于适配器类是通过继承被适配类来实现目标接口的,所以它只能适配一个被适配类,如果要适配多个被适配类,就需要创建多个适配器类,这会增加系统的复杂性。而且,类适配器使用了继承,这可能会违反一些面向对象的设计原则,如里氏替换原则等。
- 对象适配器的缺点:对象适配器需要在适配器类中创建被适配类的实例,这可能会增加系统的开销。而且,如果被适配类的接口有变化,适配器类也需要相应地进行修改,这可能会增加维护的难度。
四、应用场景
- 遗留代码的适配:当需要在新的系统中使用一些遗留代码,但遗留代码的接口与新系统的接口不兼容时,可以使用适配器模式来适配这些遗留代码。
- 第三方库的适配:当需要使用第三方库,但第三方库的接口与系统的接口不兼容时,可以使用适配器模式来适配第三方库。
- 不同类库之间的适配:当需要将两个不同类库中的类组合在一起使用,但它们的接口不兼容时,可以使用适配器模式来适配这两个类库中的类。
案例
一、支付接口适配的背景
假设一个电商平台需要支持多种支付方式。每种支付方式都有自己的接口,例如:
- 支付宝支付接口:
AlipayService
,它有方法payWithAlipay(String orderId, double amount)
。 - 微信支付接口:
WechatPayService
,它有方法payWithWechat(String orderId, double amount)
。 - 银联支付接口:
UnionPayService
,它有方法payWithUnionPay(String orderId, double amount)
。
这些支付接口虽然功能相似,但方法名和参数等细节可能有所不同,这就导致客户端代码在调用不同的支付方式时需要编写不同的代码,增加了代码的复杂性和维护难度。
二、使用适配器模式进行适配
为了解决这个问题,可以定义一个通用的支付接口(目标接口),然后为每种支付方式创建一个适配器类,将具体的支付接口(被适配接口)适配到通用的支付接口上。
(一)定义通用支付接口(目标接口)
public interface PaymentAdapter {void pay(String orderId, double amount);
}
(二)创建适配器类
- 支付宝支付适配器
public class AlipayAdapter implements PaymentAdapter {private AlipayService alipayService;public AlipayAdapter(AlipayService alipayService) {this.alipayService = alipayService;}@Overridepublic void pay(String orderId, double amount) {alipayService.payWithAlipay(orderId, amount);}
}
- 微信支付适配器
public class WechatPayAdapter implements PaymentAdapter {private WechatPayService wechatPayService;public WechatPayAdapter(WechatPayService wechatPayService) {this.wechatPayService = wechatPayService;}@Overridepublic void pay(String orderId, double amount) {wechatPayService.payWithWechat(orderId, amount);}
}
- 银联支付适配器
public class UnionPayAdapter implements PaymentAdapter {private UnionPayService unionPayService;public UnionPayAdapter(UnionPayService unionPayService) {this.unionPayService = unionPayService;}@Overridepublic void pay(String orderId, double amount) {unionPayService.payWithUnionPay(orderId, amount);}
}
(三)客户端调用
在客户端代码中,只需要使用通用的支付接口PaymentAdapter
来调用支付方法,而不需要关心具体的支付方式。这样,客户端代码就可以统一地调用不同的支付方式,提高了代码的可维护性和可扩展性。
public class PaymentClient {public static void main(String[] args) {// 假设已经创建了具体的支付服务实例AlipayService alipayService = new AlipayService();WechatPayService wechatPayService = new WechatPayService();UnionPayService unionPayService = new UnionPayService();// 创建适配器实例PaymentAdapter alipayAdapter = new AlipayAdapter(alipayService);PaymentAdapter wechatPayAdapter = new WechatPayAdapter(wechatPayService);PaymentAdapter unionPayAdapter = new UnionPayAdapter(unionPayService);// 统一调用支付方法alipayAdapter.pay("12345", 100.0);wechatPayAdapter.pay("67890", 200.0);unionPayAdapter.pay("11111", 300.0);}
}
三、适配器模式在支付接口适配中的优势
- 统一接口:通过定义一个通用的支付接口,将不同的支付方式统一起来,客户端代码只需要关心这个通用接口,而不需要关心具体的支付实现细节。
- 提高可维护性:当需要添加新的支付方式时,只需要创建一个新的适配器类,而不需要修改客户端代码。这符合开闭原则,提高了系统的可维护性。
- 降低耦合度:客户端代码与具体的支付实现解耦,客户端只需要依赖通用的支付接口,而不需要直接依赖具体的支付服务类,降低了系统的耦合度。
适配器模式在处理多种支付接口的适配问题时,能够有效地解决接口不兼容的问题,提高代码的可维护性和可扩展性,是一种非常实用的设计模式。
相关文章:
二十三种设计模式-适配器模式
适配器模式(Adapter Pattern)是一种结构型设计模式,它允许将不兼容的接口转换成客户端期望的接口,从而使原本因接口不匹配而不能一起工作的类可以协同工作。以下是关于适配器模式的详细介绍: 一、定义及作用 定义&am…...
复用类(2):代理、结合使用组合和继承
1 代理 第三种关系称为代理,这是继承与组合之间的中庸之道,因为我们将一个成员对象置于所要构造的类中(就像组合),但与此同时我们在新类中暴露了该成员对象的所有方法(就像继承)。例如ÿ…...

浅谈云计算07 | 云安全机制
云计算安全机制 一、引言二、加密技术:数据的隐形护盾三、散列机制:数据完整性的忠诚卫士四、数字签名:数据来源与真伪的鉴定专家五、公钥基础设施(PKI):信任的基石六、身份与访问管理(IAM&…...

【机器学习】零售行业的智慧升级:机器学习驱动的精准营销与库存管理
我的个人主页 我的领域:人工智能篇,希望能帮助到大家!!!👍点赞 收藏❤ 在当今数字化浪潮汹涌澎湃的时代,零售行业正站在转型升级的十字路口。市场竞争的白热化使得企业必须另辟蹊径࿰…...
深入理解 Entity、VO、QO、DTO 的区别及其在 MVC 架构中的应用
文章背景 在现代软件开发中,我们经常会接触到各种数据结构的概念,比如 Entity、VO(Value Object)、QO(Query Object)、DTO(Data Transfer Object)等。这些概念尽管看似相似ÿ…...

vue集成高德地图API实现坐标拾取功能
安装与配置: 组件 | vue-amapDescriptionhttps://elemefe.github.io/vue-amap/#/zh-cn/introduction/install简介 | vuemap/vue-amap简介https://vue-amap.guyixi.cn/zh-cn/introduction/introduction.html 我的应用 | 高德控制台高德开放平台官网控…...
Spring Boot Actuator 详细介绍
Spring Boot Actuator 详细介绍 1. 简介 Spring Boot Actuator 是 Spring Boot 提供的一个用于监控和管理应用程序的强大功能模块。它可以帮助我们了解应用程序的运行状况、指标收集、环境信息、日志级别管理等。 2. 添加依赖 2.1 在 pom.xml 中添加以下依赖: …...

联通用户管理系统(一)
#联通用户管理系统(一) 1.新建项目 如果你是windows的话,界面应该是如下的: 2.创建app python manage.py startapp app01一般情况下:我们是在pycharm的终端中运行上述指令,但是pychrm中为我们提供了工具…...

go chan底层分析
go chan底层分析 底层源码hchanmakechan 方法 环形队列阻塞机制向管道写数据流程图源码 从管道读数据流程图源码 关闭通道 底层源码 hchan type hchan struct {qcount uint // 当前队列中剩余元素个数dataqsiz uint // 环形队列长度,即可以…...
idea上git log面板的使用
文章目录 各种颜色含义具体的文件的颜色标签颜色🏷️ 节点和路线 各种颜色含义 具体的文件的颜色 红色:表示还没有 git add 提交到暂存区绿色:表示已经 git add 过,但是从来没有 commit 过蓝色:表示文件有过改动 标…...

WOA-Transformer鲸鱼算法优化编码器时间序列预测(Matlab实现)
WOA-Transformer鲸鱼算法优化编码器时间序列预测(Matlab实现) 目录 WOA-Transformer鲸鱼算法优化编码器时间序列预测(Matlab实现)预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现WOA-Transformer鲸鱼算法优化编…...
dock 制作 python环境
报错 :Get "https://registry-1.docker.io/v2/": net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers) 解决方法 配置加速地址 vim /etc/docker/daemon.json 添加以下内容 { "registry-mirror…...
2025第3周 | json-server的基本使用
目录 1. json-server是什么?2. json-server怎么用?2.1 安装2.2 创建db.json2.3 启动服务2.4 查看效果 3. 前端进行模拟交互3.1 创建demo.html3.2 创建demo.js 2025,做想做的事,读想读的书,持续学习,自律生活…...

Autodl转发端口,在本地机器上运行Autodl服务器中的ipynb文件
通过 SSH 隧道将远程端口转发到本地机器 输入服务器示例的SSH指令和密码,将远程的6006端口代理到本地 在服务器终端,激活conda虚拟环境 conda activate posecnnexport PYOPENGL_PLATFORMegljupyter notebook --no-browser --port6006 --allow-root从…...

flutter Get GetMiddleware 中间件不起作用问题
当使用 get: ^5.0.0-release-candidate-9.2.1最新版本时,中间件GetMiddleware各种教程都是让我们在redirect中实现,比如: overrideRouteSettings? redirect(String? route) {return RouteSettings(name: "/companyAuthIndexPage"…...

RabbitMQ(三)
RabbitMQ中的各模式及其用法 工作队列模式一、生产者代码1、封装工具类2、编写代码3、发送消息效果 二、消费者代码1、编写代码2、运行效果 发布订阅模式一、生产者代码二、消费者代码1、消费者1号2、消费者2号 三、运行效果四、小结 路由模式一、生产者代码二、消费者代码1、消…...

【Python】Python之locust压测教程+从0到1demo:基础轻量级压测实战(1)
文章目录 一、什么是Locust二、Locust 架构组成三、实战 Demo准备一个可调用的接口编写一个接口测试用例编写一个性能测试用例执行性能测试用例代码1、通过 Web UI 执行(GUI模式)2、通过命令行执行(非GUI模式) 小知识:…...
【JavaScript】基础内容,HTML如何引用JavaScript, JS 常用的数据类型
HTML 嵌入 Javascript 的方式 引入外部 js 文件 <head> <script Language "javaScript" src"index.js"/> </head>内部声明 <head> <script language"javascript">function hello(){alert("hello word&qu…...

vue使用自动化导入api插件unplugin-auto-import,避免频繁手动导入
unplugin-auto-import是一个现代的自动导入插件,旨在简化前端开发中的导入过程,减少手动导入的繁琐工作,提升开发效率。它支持多种构建工具,包括Vite、Webpack、Rollup和esbuild,并且可以与TypeScript配合使用&…...
在 C# 中的Lambda 表达式
在 C# 中,Lambda 表达式是用来定义匿名函数的一种简洁方式,通常用于简化代码,尤其是在 LINQ 查询、事件处理或方法作为参数的场景中。Lambda 表达式的语法如下: 基本语法 (parameters) > expression_or_statement_blockparam…...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...

label-studio的使用教程(导入本地路径)
文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...
生成 Git SSH 证书
🔑 1. 生成 SSH 密钥对 在终端(Windows 使用 Git Bash,Mac/Linux 使用 Terminal)执行命令: ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" 参数说明: -t rsa&#x…...

NFT模式:数字资产确权与链游经济系统构建
NFT模式:数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新:构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议:基于LayerZero协议实现以太坊、Solana等公链资产互通,通过零知…...
浅谈不同二分算法的查找情况
二分算法原理比较简单,但是实际的算法模板却有很多,这一切都源于二分查找问题中的复杂情况和二分算法的边界处理,以下是博主对一些二分算法查找的情况分析。 需要说明的是,以下二分算法都是基于有序序列为升序有序的情况…...

OPENCV形态学基础之二腐蚀
一.腐蚀的原理 (图1) 数学表达式:dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一,腐蚀跟膨胀属于反向操作,膨胀是把图像图像变大,而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...
力扣-35.搜索插入位置
题目描述 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...
C++.OpenGL (20/64)混合(Blending)
混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...

TSN交换机正在重构工业网络,PROFINET和EtherCAT会被取代吗?
在工业自动化持续演进的今天,通信网络的角色正变得愈发关键。 2025年6月6日,为期三天的华南国际工业博览会在深圳国际会展中心(宝安)圆满落幕。作为国内工业通信领域的技术型企业,光路科技(Fiberroad&…...