设计模式 结构型 适配器模式(Adapter Pattern)与 常见技术框架应用 解析

适配器模式(Adapter Pattern)是一种结构型设计模式,它允许将一个类的接口转换成客户端所期望的另一个接口,从而使原本因接口不兼容而无法一起工作的类能够协同工作。这种设计模式在软件开发中非常有用,尤其是在需要集成不同系统或库时,它们的接口可能并不一致。
一、核心思想
适配器模式的核心思想是通过创建一个中间层(适配器),使得原本由于接口不兼容而无法一起工作的类可以协同工作。这个中间层负责将源接口转换为目标接口,从而在客户端和目标类之间提供一个桥梁。
二、定义与结构
定义:将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
结构:
- 目标接口(Target):定义了客户端期望的接口。
- 被适配者(Adaptee):需要被适配的类,其接口与目标接口不兼容。
- 适配器(Adapter):实现了目标接口,并持有被适配者的实例。适配器通过调用被适配者的方法来实现目标接口的方法。
角色
在适配器模式中,通常包含以下角色:
- 目标角色(Target):定义了客户端需要使用的接口。
- 源角色(Adaptee):需要被适配的接口,它与目标接口不兼容。
- 适配器角色(Adapter):负责将源接口转换成目标接口,使得客户端可以通过目标接口与适配器交互,而无需知道具体的被适配者。
- 客户类(Client):在客户类中针对目标抽象类进行编程,调用在目标抽象类中定义的业务方法。
三、实现步骤及代码示例
1、定义目标接口
public interface MediaPlayer {void play(String audioType, String fileName);
}
这里定义了一个简单的媒体播放器接口 MediaPlayer,它期望能播放指定音频类型和文件名的音频文件。
2、定义适配者类
public class AdvancedMediaPlayer {public void playVlc(String fileName) {System.out.println("Playing vlc file: " + fileName);}public void playMp4(String fileName) {System.out.println("Playing mp4 file: " + fileName);}
}
AdvancedMediaPlayer 是已有的高级媒体播放器类,能播放 VLC 和 MP4 格式文件,但接口与 MediaPlayer 不同,是需要适配的对象。
3、实现适配器类
public class MediaAdapter implements MediaPlayer {private AdvancedMediaPlayer advancedMediaPlayer;public MediaAdapter(String audioType) {if ("vlc".equals(audioType)) {advancedMediaPlayer = new AdvancedMediaPlayer();advancedMediaPlayer.playVlc(audioType);} else if ("mp4".equals(audioType)) {advancedMediaPlayer = new AdvancedMediaPlayer();advancedMediaPlayer.playMp4(audioType);}}@Overridepublic void play(String audioType, String fileName) {if ("vlc".equals(audioType)) {advancedMediaPlayer.playVlc(fileName);} else if ("mp4".equals(audioType)) {advancedMediaPlayer.playMp4(fileName);}}
}
MediaAdapter 实现了 MediaPlayer 目标接口,内部根据传入音频类型实例化 AdvancedMediaPlayer,并在 play 方法中调用适配者对应方法来播放文件,完成接口适配。
4、客户端使用示例
public class AudioPlayer implements MediaPlayer {private MediaAdapter mediaAdapter;@Overridepublic void play(String audioType, String fileName) {if ("mp3".equals(audioType)) {System.out.println("Playing mp3 file: " + fileName);} else if (("vlc".equals(audioType)) || ("mp4".equals(audioType))) {mediaAdapter = new MediaAdapter(audioType);mediaAdapter.play(audioType, fileName);} else {System.out.println("Invalid audio type");}}
}public class Main {public static void main(String[] args) {AudioPlayer audioPlayer = new AudioPlayer();audioPlayer.play("mp3", "song.mp3");audioPlayer.play("vlc", "video.vlc");audioPlayer.play("mp4", "movie.mp4");}
}
AudioPlayer 作为客户端类,它本身能播放 MP3 文件,对于 VLC 和 MP4 文件则借助 MediaAdapter 适配,在 main 程序入口,演示了多种音频格式播放,体现适配器模式使不同接口协同工作。
四、常见技术框架应用
1、在 Python 的 Django 框架中的应用
在 Django 项目中,若要整合第三方认证系统,其返回的用户数据格式与 Django 内置的用户模型格式不一致。假设第三方认证返回用户信息是一个字典 {"name": "John", "email": "john@example.com", "age": 30},而 Django 用户模型期望通过实例化 User 类,传入 username、email 等参数来创建用户。
from django.contrib.auth.models import User# 适配者类,模拟第三方认证返回数据格式
class ThirdPartyUserData:def __init__(self, user_data):self.user_data = user_datadef get_name(self):return self.user_data["name"]def get_email(self):return self.user_data["email"]def get_age(self):return self.user_data["age"]# 适配器类
class DjangoUserAdapter:def __init__(self, third_party_user_data):self.third_party_user_data = third_party_user_datadef create_django_user(self):name = self.third_party_user_data.get_name()email = self.third_party_user_data.get_email()username = name.lower().replace(" ", "")user = User.objects.create(username=username, email=email)return user# 客户端使用
third_party_user_data = ThirdPartyUserData({"name": "John Doe", "email": "johndoe@example.com", "age": 30})
adapter = DjangoUserAdapter(third_party_user_data)
new_user = adapter.create_django_user()
print(new_user)
这里 ThirdPartyUserData 是适配者,提供第三方原始用户数据格式。DjangoUserAdapter 是适配器,将第三方数据转换为符合 Django 用户模型创建的格式,使第三方认证能无缝接入 Django 项目。
2、在 JavaScript 的 Vue.js 框架中的应用
假设在 Vue 项目中有一个旧的图表组件 OldChart,它接收的数据格式是一个包含 labels 和 values 的二维数组 [["label1", "label2"], ["value1", "value2"]],但新的业务需求要求使用 echarts 库绘制图表,echarts 所需数据格式是一个对象 { xAxis: ["label1", "label2"], yAxis: ["value1", "value2"] }。
<template><div id="app"><old-chart ref="oldChart" :data="oldData" /><echarts ref="echarts" :data="echartsData" /></div>
</template><script>
import OldChart from './OldChart.vue';
import echarts from 'echarts';// 适配者:旧图表组件
export default {components: { OldChart },data() {return {oldData: [["January", "February"], ["10", "20"]],echartsData: null};},mounted() {// 适配器const adapter = {convertData(oldData) {const [labels, values] = oldData;return { xAxis: labels, yAxis: values };}};const echartsData = adapter.convertData(this.oldData);this.echartsData = echartsData;const myChart = echarts.init(this.$refs.echarts.$el);myChart.setOption({xAxis: {type: 'category',data: echartsData.xAxis},yAxis: {type: 'value'},series: [{data: echartsData.yAxis,type: 'bar'}]});}
};
</script>
这里 OldChart 是适配者,adapter 的 convertData 方法作为适配器功能,将旧数据格式转换为 echarts 需要的格式,使两种图表组件能在同一项目中共存并按需使用。
五、应用场景
- 旧系统的兼容性问题:当需要使用一个已有系统,但它的接口与新系统不兼容时,可以通过适配器模式进行适配。
- 第三方库整合:当使用第三方库的接口与当前项目需求不一致时,可以通过适配器封装以符合需求。
- 统一接口:在多种类似功能的接口中,适配器可以对不同实现进行封装,提供统一的访问接口。
- 数据格式转换:在不同数据格式之间进行转换,如将JSON数据转换为XML数据。
- 硬件设备驱动:将不同厂商的硬件设备接口统一适配为系统标准接口。
- 图像绘制系统:现有一个老版本的绘图类LegacyRenderer,需要将其适配到新的绘图接口NewRenderer,以兼容新功能。
- 支付系统整合:整合多个第三方支付接口(如PayPal、Stripe)到统一的支付系统中。
- 日志框架适配器:将不同日志框架的接口统一适配为系统标准接口。
- 数据库适配器:将不同数据库供应商的API转换为统一的数据库访问接口,以便在不同的数据库之间切换和使用。
- 网络通信协议转换:在不同网络通信协议之间进行转换,如将HTTP请求转换为WebSocket请求。
- 操作系统平台差异处理:在不同操作系统平台(如Windows和Linux)之间进行文件路径或命令行参数的差异处理。
- 多媒体文件格式转换:在不同多媒体文件格式之间进行转换,如将MP3文件转换为WAV文件。
- 用户界面适配:在不同用户界面风格或布局之间进行适配,以满足不同用户的需求。
- 游戏控制器适配:将不同品牌或型号的游戏控制器适配为统一的游戏控制接口。
- 虚拟设备模拟:在软件开发过程中,模拟不存在的硬件设备或软件组件,以便进行测试或开发。
- 云服务集成:将不同云服务提供商的API转换为统一的接口,以便在多个云服务提供商之间无缝切换。
- 跨语言编程:在不同编程语言之间进行互操作,如将Java对象转换为Python对象。
- 消息队列集成:将不同消息队列系统的接口统一适配为系统标准接口。
- 缓存策略适配:将不同缓存策略的实现统一适配为系统标准接口。
- 安全认证机制适配:将不同安全认证机制的实现统一适配为系统标准接口。
- 国际化支持:将不同国际化方案的实现统一适配为系统标准接口。
- 时间日期格式转换:在不同时间日期格式之间进行转换,如将UNIX时间戳转换为人类可读的日期格式。
- 货币汇率转换:在不同货币之间进行汇率转换。
- 单位换算:在不同单位之间进行换算,如将英里转换为公里。
- 传感器数据适配:将不同传感器的数据格式统一适配为系统标准接口。
- 配置文件解析:将不同配置文件格式的解析结果统一适配为系统标准接口。
六、优缺点
优点:
- 提高复用性:适配者类往往是已有且经过实践检验的代码,适配器模式让其能在新的目标接口场景下复用,避免重复开发类似功能。
- 增强系统扩展性:当需要接入新的不兼容模块或接口时,只需新增适配器类,无需改动原有系统核心代码,符合开闭原则,便于系统持续演进。
- 解耦系统组件:将接口转换逻辑封装在适配器中,使目标接口与适配者独立发展,降低两者直接耦合度,系统各部分维护、升级更便利。
缺点:
- 额外的复杂性:引入适配器增加了系统类的数量与层次,如果过度使用,会使代码结构略显复杂,尤其在调试时,需追踪适配器内部逻辑及适配者原始接口,增加理解成本。
- 性能损耗:适配器在运行时需进行接口转换、数据格式调整等操作,相较于直接调用原生兼容接口,可能会有一定的性能损失,不过在多数非性能敏感场景下可接受。

相关文章:
设计模式 结构型 适配器模式(Adapter Pattern)与 常见技术框架应用 解析
适配器模式(Adapter Pattern)是一种结构型设计模式,它允许将一个类的接口转换成客户端所期望的另一个接口,从而使原本因接口不兼容而无法一起工作的类能够协同工作。这种设计模式在软件开发中非常有用,尤其是在需要集成…...
vue 项目集成 electron 和 electron 打包及环境配置
vue electron 开发桌面端应用 安装 electron npm i electron -D记得加上-D,electron 需添加到devDependencies,如果添加到dependencies后面运行可能会报错 根目录创建electron文件夹,在electron文件夹创建main.js(或者backgrou…...
vscode如何离线安装插件
在没有网络的时候,如果要安装插件,就会麻烦一些,需要通过离线安装的方式进行。下面记录如何在vscode离线安装插件。 一、下载离线插件 在一台能联网的电脑中,下载好离线插件,拷贝到无法联网的电脑上。等待安装。 vscode插件商店地址:https://marketplace.visualstudio.co…...
计算机网络常见面试题及解答
以下是计算机网络中常见的面试题及解答,按主题分类: --- ## **一、基础概念** ### **1. OSI 七层模型和 TCP/IP 模型的区别是什么?** **答:** - **OSI 七层模型:** - 应用层、表示层、会话层、传输层、网络层、数…...
举例说明AI模型怎么聚类,最后神经网络怎么保存
举例说明怎么聚类,最后神经网络怎么保存 目录 举例说明怎么聚类,最后神经网络怎么保存K - Means聚类算法实现神经元特征聚类划分成不同专家的原理和过程 特征提取: 首先,需要从神经元中提取有代表性的特征。例如,对于一个多层感知机(MLP)中的神经元,其权重向量可以作为特…...
HarmonyOS NEXT应用开发实战(一):边学边玩,从零开发一款影视APP
引言 学习一项技能,最好也最快的办法就是动手实战。通过自己给自己找项目练习,不仅能够激发兴趣,还能从开发实战中不断总结经验。这种学习方法是最为高效的。今天,我们将通过开发一款名为“爱影家”的影视APP,来学习H…...
STM32G0B1 can Error_Handler 解决方法
问题现象 MCU上电,发送0x13帧数据固定进入 Error_Handler 硬件介绍 MCU :STM32G0B1 can:NSI1042 tx 接TX RX 接RX 折腾了一下午,无解,问题依旧; 对比测试 STM32G431 手头有块G431 官方评估版CAN 模块; 同样的…...
使用 `llama_index` 构建智能问答系统:多种文档切片方法的评估
使用 llama_index 构建智能问答系统:多种文档切片方法的评估 代码优化与解析1. **代码结构优化**2. **日志管理**3. **环境变量管理**4. **模型初始化**5. **提示模板更新**6. **问答函数优化**7. **索引构建与查询引擎**8. **节点解析器测试** 总结 在现代自然语言…...
【大模型】7 天 AI 大模型学习
7 天 AI 大模型学习 Day 2 今天是 7 天AI 大模型学习的第二天 😄,今天我将会学习 Transformer 、Encoder-based and Decoder-Based LLMs 等 。如果有感兴趣的,就和我一起开始吧 ~ 课程链接 :2025年快速吃透AI大模型&am…...
软件工程大复习之(四)——面向对象与UML
4.1 面向对象概述 面向对象(OO)是一种编程范式,它将数据和处理数据的方法封装在对象中。面向对象的主要概念包括: 对象:实例化的数据和方法的集合。类:对象的蓝图或模板。封装:隐藏对象的内部…...
【Linux】shell命令
目录 shell的基本命令 shell - 贝壳 外在保护工具 用户、shell、内核、硬件之间的关系 解析器的分类: shell命令格式 history -历史记录查询 修改环境变量的值: shell中的特殊字符 通配符 管道 | 输入输出重定向 命令置换符 shell的基本命…...
ValuesRAG:以检索增强情境学习强化文化对齐
随着大型语言模型(LLMs)的迅猛发展,其在各个领域展现出强大的能力。然而,训练数据中西方中心主义的倾向,使得 LLMs 在文化价值观一致性方面面临严峻挑战,这一问题在跨文化场景中尤为突出,可能导…...
【机器学习篇】交通革命:机器学习如何引领未来的道路创新
嘿,你知道吗?机器学习正在交通领域掀起一场革命啦!它将如何引领未来道路创新呢 本文有精彩的 C 代码演示、实用的图片解释,还有超多干货,保证让你大开眼界,点赞收藏关注, 开启一场奇妙的探索之…...
DeepSeek-V3 通俗详解:从诞生到优势,以及与 GPT-4o 的对比
1. DeepSeek 的前世今生 1.1 什么是 DeepSeek? DeepSeek 是一家专注于人工智能技术研发的公司,致力于打造高性能、低成本的 AI 模型。它的目标是让 AI 技术更加普惠,让更多人能够用上强大的 AI 工具。 1.2 DeepSeek-V3 的诞生 DeepSeek-V…...
把vue项目或者vue组件发布成npm包或者打包成lib库文件本地使用
将vue项目发布成npm库文件,第三方通过npm依赖安装使用;使用最近公司接了一个项目,这个项目需要集成到第三方页面,在第三方页面点击项目名称,页面变成我们的项目页面;要求以npm库文件提供给他们;…...
【STC库函数】Compare比较器的使用
如果我们需要比较两个点的电压,当A点高于B点的时候我们做一个操作,当B点高于A点的时候做另一个操作。 我们除了加一个运放或者比较器,还可以直接使用STC内部的一个比较器。 正极输入端可以是P37、P50、P51,或者从ADC的十六个通道…...
单片机-独立按键矩阵按键实验
1、按键介绍 按键管脚两端距离长的表示默认是导通状态,距离短的默认是断开状态, 如果按键按下,初始导通状态变为断开,初始断开状态变为导通 我们开发板是采用软件消抖,一般来说一个简单的按键消抖就是先读取按键的状…...
若要把普通表转成分区表,就需要先新建分区表,然后把普通表中的数据导入新建分区表。 具体怎么导入?
将普通表转换为分区表并导入数据是一个常见的数据库管理任务。以下是详细的步骤和示例,帮助你在 GaussDB 中完成这一过程: 1. 创建分区表 首先,你需要创建一个新的分区表,定义好分区键和分区策略。假设你有一个普通表 orders&am…...
XXX公司面试真题
一、一面问题 1.线程池的主要参数 核心线程数最大线程数空闲线程存活时间存活时间单位任务队列线程工厂拒绝策略允许核心线程超时 2. 线程的状态 新建状态就绪状态运行状态阻塞状态死亡状态 补充:线程阻塞的原因 线程调用sleep()方法进入睡眠状态 线程得到一个…...
第一节:电路连接【51单片机+A4988+步进电机教程】
摘要:本节介绍如何搭建一个51单片机A4988步进电机控制电路,所用材料均为常见的模块,简单高效的方式搭建起硬件环境 一、硬件清单 ①51单片机最小控制模块 ②开关电源 ③A4988模块转接座 ④二相四线步进电机 ⑤电线若干 二、接线 三、A49…...
铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...
MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例
一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...
3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...
【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/
使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题:docker pull 失败 网络不同,需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...
【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)
1.获取 authorizationCode: 2.利用 authorizationCode 获取 accessToken:文档中心 3.获取手机:文档中心 4.获取昵称头像:文档中心 首先创建 request 若要获取手机号,scope必填 phone,permissions 必填 …...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...
QT3D学习笔记——圆台、圆锥
类名作用Qt3DWindow3D渲染窗口容器QEntity场景中的实体(对象或容器)QCamera控制观察视角QPointLight点光源QConeMesh圆锥几何网格QTransform控制实体的位置/旋转/缩放QPhongMaterialPhong光照材质(定义颜色、反光等)QFirstPersonC…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...
ubuntu22.04有线网络无法连接,图标也没了
今天突然无法有线网络无法连接任何设备,并且图标都没了 错误案例 往上一顿搜索,试了很多博客都不行,比如 Ubuntu22.04右上角网络图标消失 最后解决的办法 下载网卡驱动,重新安装 操作步骤 查看自己网卡的型号 lspci | gre…...
