适配器模式:化解接口不兼容的桥梁设计
适配器模式:化解接口不兼容的桥梁设计
一、模式核心:让不兼容的接口协同工作
在软件开发中,经常会遇到接口不兼容的情况:如旧系统提供的接口无法被新系统直接调用,或第三方库的接口与当前系统设计不匹配。适配器模式(Adapter Pattern) 通过创建一个中间适配器,将一个接口转换为另一个接口,使原本不兼容的类可以协同工作,核心解决:
- 接口转换:将一个类的接口适配为另一个接口
- 复用 legacy 代码:在不修改原有代码的前提下,让旧组件兼容新系统
核心思想与 UML 类图(PlantUML 语法)
适配器模式分为对象适配器(组合方式)和类适配器(继承方式),典型的对象适配器结构如下:
二、核心实现:适配旧支付接口到新系统
1. 定义目标接口(新系统期望的接口)
// 新系统使用的支付接口
public interface PaymentTarget {void pay(String orderId, double amount); // 支付方法
}
2. 现有适配者(旧系统的支付接口,方法名不兼容)
// 旧系统支付接口(方法名为payOld)
public class LegacyPayment {public void payOld(String orderId, double amount) {System.out.println("旧支付接口处理订单:" + orderId + ",金额:" + amount);}
}
3. 创建对象适配器(通过组合适配旧接口)
public class PaymentAdapter implements PaymentTarget {private LegacyPayment legacyPayment; // 持有旧接口对象public PaymentAdapter(LegacyPayment legacyPayment) {this.legacyPayment = legacyPayment;}@Overridepublic void pay(String orderId, double amount) {// 将新接口的pay方法适配为旧接口的payOld方法legacyPayment.payOld(orderId, amount);}
}
4. 客户端使用适配器(透明调用新接口)
public class ClientDemo {public static void main(String[] args) {// 创建旧接口对象LegacyPayment legacyPayment = new LegacyPayment();// 创建适配器,将旧接口适配为新接口PaymentTarget adapter = new PaymentAdapter(legacyPayment);// 通过新接口调用支付功能adapter.pay("ORDER_20231001", 998.0);}
}
三、进阶:实现双向适配器与多接口适配
1. 双向适配器(同时适配两个方向的接口)
// 新接口(目标接口)
public interface NewService {void newMethod();
}// 旧接口(适配者)
public interface OldService {void oldMethod();
}// 双向适配器
public class BidirectionalAdapter implements NewService, OldService {private NewService newService;private OldService oldService;public BidirectionalAdapter(NewService newService, OldService oldService) {this.newService = newService;this.oldService = oldService;}// 适配旧接口到新接口@Overridepublic void newMethod() {oldService.oldMethod();}// 适配新接口到旧接口@Overridepublic void oldMethod() {newService.newMethod();}
}
2. 多接口适配器(适配多个不兼容接口)
public interface LogService {void log(String message);
}public interface NotifyService {void sendNotification(String message);
}public class LegacySystem {public void writeToLog(String logMsg) { /* 旧日志方法 */ }public void sendAlert(String alertMsg) { /* 旧通知方法 */ }
}public class MultiAdapter implements LogService, NotifyService {private LegacySystem legacySystem;public MultiAdapter(LegacySystem legacySystem) {this.legacySystem = legacySystem;}@Overridepublic void log(String message) {legacySystem.writeToLog("LOG: " + message);}@Overridepublic void sendNotification(String message) {legacySystem.sendAlert("ALERT: " + message);}
}
3. 可视化适配流程
四、框架与源码中的适配器实践
1. Spring MVC 适配器(HandlerAdapter)
- 适配不同类型的处理器(Handler),如
Controller
、HttpRequestHandler
// 适配@Controller标注的类
public class AnnotationMethodHandlerAdapter implements HandlerAdapter {@Overridepublic boolean supports(Object handler) {return handler instanceof Controller;}@Overridepublic ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) {// 将请求适配到Controller的方法return handleInternal(request, response, (Controller) handler);}
}
2. AWT/Swing 事件适配器(Event Adapter)
- 简化事件监听器的实现,如
MouseAdapter
提供空方法实现
// 继承MouseAdapter,仅重写需要的方法
public class MyMouseListener extends MouseAdapter {@Overridepublic void mouseClicked(MouseEvent e) {// 处理点击事件}
}
3. 数据库驱动适配器(JDBC Driver)
- JDBC 驱动将不同数据库的原生接口适配为统一的 JDBC 接口
// MySQL驱动适配器(简化示例)
public class MySQLDriver implements Driver {@Overridepublic Connection connect(String url, Properties info) {// 适配MySQL的原生连接方法return new MySQLConnection(url, info);}
}
五、避坑指南:正确使用适配器模式的 3 个要点
1. 优先使用对象适配器(组合优于继承)
- ❌ 类适配器需要继承适配者类,若适配者是 final 类则无法使用
- ✅ 对象适配器通过组合适配者对象,更灵活且符合合成复用原则
2. 避免过度适配(保持接口简洁)
- 适配器应聚焦于接口转换,避免包含复杂业务逻辑
- 复杂转换逻辑可提取为独立的
Converter
工具类
3. 明确适配范围(避免职责扩散)
- 一个适配器应只适配一个适配者或一组相关接口
- 若需要适配多个无关接口,考虑拆分为多个适配器
4. 反模式:滥用适配器掩盖设计缺陷
- 若系统中存在大量适配器,可能是接口设计不合理导致,需重新审视架构
六、总结:何时该用适配器模式?
适用场景 | 核心特征 | 典型案例 |
---|---|---|
复用遗留系统接口 | 旧接口与新系统不兼容,但无法修改旧代码 | 整合 ERP 系统、迁移老旧微服务 |
使用第三方库 | 第三方库接口与现有系统设计不匹配 | 集成支付网关、物流 API |
统一不同接口格式 | 需要将多种接口转换为统一的公共接口 | 日志系统(适配不同日志格式) |
适配器模式通过 “接口转换 + 中间层封装” 的设计,为不兼容的接口搭建了沟通的桥梁,是解决系统集成问题的有效方案。下一篇我们将深入探讨桥接模式,解析如何分离抽象与实现的独立变化,敬请期待!
扩展思考:适配器模式 vs 外观模式
两者都涉及对接口的封装,但核心目标不同:
模式 | 封装目的 | 接口关系 | 典型应用 |
---|---|---|---|
适配器模式 | 转换接口格式,解决不兼容问题 | 适配者与目标接口独立 | 旧系统对接、API 格式转换 |
外观模式 | 提供简化的统一接口 | 外观接口整合多个子系统接口 | 框架入口、复杂模块简化调用 |
理解这种差异,能帮助我们在设计时选择更合适的模式来优化系统结构。
相关文章:
适配器模式:化解接口不兼容的桥梁设计
适配器模式:化解接口不兼容的桥梁设计 一、模式核心:让不兼容的接口协同工作 在软件开发中,经常会遇到接口不兼容的情况:如旧系统提供的接口无法被新系统直接调用,或第三方库的接口与当前系统设计不匹配。适配器模式…...
科大讯飞Q1营收46.6亿同比增长27.7%,扣非净利同比增长48.3%
4月21日盘后,AI龙头科大讯飞(002230.SZ)发布2024年报,公司全年实现营业收入233.43亿元,同比增长18.79%,同期归母净利润为5.6亿元。 公司核心赛道业务保持快速增长,消费者、教育、汽车、医疗业务…...
基于大模型的血栓性外痔全流程风险预测与治疗管理研究报告
目录 一、引言 1.1 研究背景与目的 1.2 研究意义 二、血栓性外痔概述 2.1 定义与发病机制 2.2 临床表现与诊断方法 2.3 现有治疗手段综述 三、大模型在血栓性外痔预测中的应用原理 3.1 大模型技术简介 3.2 模型构建与训练数据来源 3.3 模型预测血栓性外痔的工作流程…...
Transformer系列(三):编码器—解码器架构
Transformer架构 一、多头自注意力 (Multi-head self-attention)将矩阵维度从d降到d/k的优点 二、层归一化 (Lary Norm)三、残差连接 (Residual Connections)Add and Norm 四、注意力对数几率缩放 (Attention logit scaling)五、T…...
C++入门小馆: 深入string类(二)
嘿,各位技术潮人!好久不见甚是想念。生活就像一场奇妙冒险,而编程就是那把超酷的万能钥匙。此刻,阳光洒在键盘上,灵感在指尖跳跃,让我们抛开一切束缚,给平淡日子加点料,注入满满的pa…...
MCU开发学习记录10 - 高级定时器学习与实践(HAL库)—PWM互补输出、死区控制、刹车控制 - STM32CubeMX
本文将介绍高级定时器的概念(只讲解高级定时器特有功能)、相关函数以及STM32CubeMX生成定时器的配置函数以及对生成定时器的配置函数进行分析(包括结构体配置、相关寄存器配置)。针对于高级定时器实践:实现输出PWM互补…...
pytest基础-new
规范 1、首先创建 py 文件命名以 test_ 开始或者以 _test 结尾 2、若是新建类,测试类需要以 Test_开头 3、测试用例(方法)需要以 test_开头 assert 断言 assert xx:判断 xx 为真 assert not xx:判断 xx 不为真 asse…...
利用Qt创建一个模拟问答系统
界面: 添加了聊天显示区域(QTextEdit) 添加了发送按钮和清空对话按钮 优化了布局和窗口大小添加了时间戳显示 2、功能: 支持实时对话可以清空对话历史 支持按回车发送消息 添加了简单的关键词匹配响应系统 交互体验&#x…...
CCF CSP 第37次(2025.03)(1_数值积分_C++)
CCF CSP 第37次(2025.03)(1_数值积分_C) 解题思路:思路一: 代码实现代码实现(思路一): 时间限制: 1.0 秒 空间限制: 512 MiB 原题链接 解题思路…...
使用 Flutter 遇坑小计
前言 首先, 谷哥很贴心地为国内用户准备了一份使用手册 不过很遗憾 就算你照着它的手册来了, 还是会在后续使用中遇到其它的坑 今天我踩了, 保不齐明天就是其他人(lol) running gradle task ‘assembledebug’ stuck 首先去确定下当下Android Studio(或者说你目前的Flutter项…...
C语言中的双链表和单链表详细解释与实现
C语言中的双链表详细解释与实现 双链表(Doubly Linked List)是一种常见的数据结构,它比单链表更灵活,因为每个节点都包含指向前驱和后继节点的指针。 双链表的基本结构 节点定义 c 复制 下载 typedef struct Node {int dat…...
CSS零基础入门笔记:狂神版
前言 本笔记是学习狂神的java教程,建议配合视频,学习体验更佳。 【狂神说Java】HTML5完整教学通俗易懂_哔哩哔哩_bilibili 第1-2章:Java零基础入门笔记:(1-2)入门(简介、基础知识)-CSDN博客 第3章&…...
使用python调用deepseek 多轮对话,详细讲解
以下代码实现在python中与deepseek实现多轮对话。 Messages参数是对话历史和上下文的核心载体。 数据结构: • 必须是包含消息对象的数组 • 每个消息对象必须包含: o role:发言者身份(system/user/assistant) o con…...
requestAnimationFrame是什么?【前端】
亲爱的读者,希望今天的你好心情~ 目录 requestAnimationFrame是什么?目的?举个栗子: requestAnimationFrame是什么? requestAnimationFrame 是一种用于优化动画性能的 JavaScript API。它允许你在浏览器的下一次重绘之…...
SQL Server基础
二. SQL Server数据库 1. 数据库简介 数据库本质是写成磁盘文件,在硬盘中存储用处 存储大量数据,方便检索和访问保持数据信息的一致、完整共享和安全通过组合分析,产生新的有用信息 数据库发展史 萌芽阶段:文件系统,…...
Vue-组件的懒加载,按需加载
在Vue项目中实现组件的懒加载(也称为按需加载或代码分割),可以大大提升应用的加载速度和性能。懒加载主要通过Webpack的代码分割功能实现,特别是使用动态导入(import()语法)。 为什么要使用懒加载…...
Docker 镜像、容器和 Docker Compose的区别
前言:Docker 的镜像、容器和 Docker Compose 是容器化技术的核心组件,以下是对它们的详细解析及使用场景说明。 1、Docker 镜像(Image) 定义: 镜像是只读模板,包含运行应用程序所需的代码、…...
批量创建同名文件夹并整理文件至对应文件夹
在我们的日常工作和生活中,创建文件几乎是必不可少的一部分。随着时间的推移,电脑中积累了大量的文件,管理这些文件成为一项繁琐且耗时的任务。尤其当我们需要按照一定规则对文件进行整理时,很多人会感到困惑。例如,领…...
每日一题——数据中心网络地址规划
文章目录 数据中心网络地址规划问题描述输入格式输出格式示例输入示例输出实现思路1. IP地址转换2. 区间排序3. 动态规划 C语言完整实现(含详细注释)总结 数据中心网络地址规划 问题描述 你是一名数据中心网络地址规划人员。每个业务需要一段IP地址区间…...
如何远程访问家中服务器-FRP内网穿透详细
💡 本文会带给你 如何远程访问家中服务器FRP自动运行的方法一、准备工作 准备一台具备公网 IP 的服务器(如阿里云、腾讯云等云服务器,要求不高,几十块一年服务的轻型服务就行),用于部署 FRP 服务端(frps)。 家中电脑(内网设备),用于运行 FRP 客户端(frpc)。 下…...
el-select+vue-virtual-scroller解决数据量大卡顿问题
解决el-select中数据量过大时,显示及搜索卡顿问题,及正确的回显默认选中数据 粗略的封装了组件,有需要各种属性自定义的,自己添加设置下 环境 node 16.20.1 npm 8.19.4 vue2、element-ui "vue-virtual-scroller"…...
数码管静态显示一位字符(STC89C52单片机)
#include <reg52.h> sbit ADDR0 P1^0; sbit ADDR1 P1^1; sbit ADDR2 P1^2; sbit ADDR3 P1^3; sbit ENLED P1^4; //用数组来存储数码管的真值表,数组将在下一章详细介绍 unsigned char code LedChar[] { 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82…...
明心见性与真如三昧
在佛教禅宗体系中,明心见性与真如三昧是修行者追求的核心目标。明心见性作为修行的起点,揭示了众生的真如本性;而真如三昧则是巩固和深化这一证悟的关键修行方法。二者相辅相成,构成了禅宗修行的完整路径。 一、明心见性…...
并发设计模式之双缓冲系统
双缓冲的本质是 通过空间换时间,通过冗余的缓冲区解决生产者和消费者的速度差异问题,同时提升系统的并发性和稳定性。 双缓冲的核心优势 优势具体表现解耦生产与消费生产者和消费者可以独立工作,无需直接同步。提高并发性生产者和消…...
使用谷歌浏览器自带功能将网页转换为PDF文件
将自己喜欢的技术网页转换为PDF文件有多种方法,这里介绍使用谷歌浏览器自带功能。 (本文截图仅作为演示使用) 使用谷歌浏览器自带功能 1.用Chrome浏览器,打开需要转换的网页,点击浏览器右上角的三个点(菜…...
JavaEE--2.多线程
1.认识线程(Thread) 1.1概念 1)什么是线程 ⼀个线程就是⼀个 "执行流". 每个线程之间都可以按照顺序执行自己的代码. 多个线程之间 "同时" 执行着多份代码. 2)为什么要有线程 首先, "并发编程" 成为 "刚需". 1. 单核 CPU 的发展遇到…...
沐渥氮气柜控制板温湿度氧含量氮气流量四显智控系统
氮气柜控制板通常用于实时监控和调节柜内环境参数,确保存储物品如电子元件、精密仪器、化学品等,处于低氧、干燥的稳定状态。以下是沐渥氮气柜控制板核心参数的详细介绍及控制逻辑: 一、控制板核心参数显示模块 1)温度显示&am…...
线性代数-矩阵的秩
矩阵的秩(Rank)是线性代数中的一个重要概念,表示矩阵中线性无关的行(或列)的最大数量。它反映了矩阵所包含的“有效信息”的维度,是矩阵的核心特征之一。 直观理解 行秩与列秩: 行秩࿱…...
【自然语言处理与大模型】模型压缩技术之量化
在这篇文章中想和大家分享什么是量化?为什么要量化?以及如何实现量化?通过这三个基本问题,我们不仅可以全面了解量化的内涵和外延,还能更清晰地认识到它在实践中的重要性和应用价值。 一、什么是量化呢? 大…...
OneClicker脚本自动运行工具
工作的时候,有很多琐碎的事情需要重复的做 比如打开某个文件,打开某个网站,打开某个软件 这个时候可以写个自动脚本,把机械琐碎的事情交给脚本处理 但是脚本一多,不好管理,而且要选择哪个脚本也是个麻烦的事…...
Activity之间交互
Backgroud: 想要实现Activity之间的交互,需要用到intent工具 本博客中所有第二Activity均为SecActivity,需要预先进行创建 本博客所使用的开发语言为Kotlin 使用intent调用Activity 显式调用 val intent Intent(this, SecActivity::class.…...
【React】搜索时高亮被搜索选中的文案
文章目录 代码实现 代码实现 函数封装: export function highlightKeyword(input: string, keyword: string | undefined) {if (!keyword || !input || !input.includes(keyword)) return input;const startIndex input.indexOf(keyword);return React.createEle…...
MCP(Minecraft Coder Pack)完全指南:从入门到精通
1. 什么是MCP? Minecraft Coder Pack(简称MCP)是一套用于反编译、修改和重新编译Minecraft Java版源代码的工具集。它允许开发者深入研究Minecraft的底层代码,并在此基础上进行模组(Mod)开发、代码分析或自…...
stm32week12
stm32学习 九.stm32与HAL库 2.HAL库框架 总架构: 文件介绍: ppp是某一外设,ex是拓展功能 HAL库API函数和变量命名规则: HAL库对寄存器位操作的相关宏定义: HAL库的回调函数: 3.STM32启动过程 MDK编译过…...
mindspeed-rl使用注意事项
1、安装 参考1:docs/install_guide.md R1-CLM/MindSpeed-RL - Gitee.com 参考2:VLLM x Ascend框架_vllm-ascend-CSDN博客 2、SFT微调 整体参考docs/supervised_finetune.md 自定义数据格式同:AUTO-DL 910B mindspeed-llm 4层DeepSeek …...
第 4 篇:平稳性 - 时间序列分析的基石
第 4 篇:平稳性 - 时间序列分析的基石 在上一篇中,我们学习了如何将时间序列分解为趋势、季节性和残差。我们看到,很多真实世界的时间序列(比如 CO2 浓度)都包含明显的趋势(长期向上或向下)和/…...
KRaft面试思路引导
Kafka实在2.8之后就用KRaft进行集群管理了 Conroller负责选举Leader,同时Controller管理集群元数据状态信息,并将元数据信息同步给各个分区的Leader 和Zookeeper管理一样,会选出一个Broker作为Controller去管理整个集群,但是元数…...
怎么建立自然语言领域的评价标准
怎么建立自然语言领域的评价标准 明确评价目标与对象 首先要清晰界定评价的目标,比如是评估模型对文本语义的理解能力、生成文本的质量,还是系统在信息检索中的表现等。同时,明确评价对象,可能是一个语言模型、一个问答系统、一个机器翻译工具等。确定评价维度与指标 语言…...
EMQX学习笔记
MQTT简介 MQTT是一种基于发布订阅模式的消息传输协议 消息:设备和设备之间传输的数据,或者服务和服务之间传输的数据 协议:传输数据时所遵循的规则 轻量级:MQTT协议占用的请求源较少,数据报文较小 可靠较强ÿ…...
组件是怎样写的(1):虚拟列表-VirtualList
本篇文章是《组件是怎样写的》系列文章的第一篇,该系列文章主要说一下各组件实现的具体逻辑,组件种类取自 element-plus 和 antd 组件库。 每个组件都会有 vue 和 react 两种实现方式,可以点击 https://hhk-png.github.io/components-show/ …...
CGAL 计算直线之间的距离(3D)
文章目录 一、简介二、实现代码三、实现效果一、简介 这里的计算思路很简单: 1、首先将两个三维直线均平移至过原点处,这里两条直线可以构成一个平面normal。 2、如果两个直线平行,那么两条直线之间的距离就转换为直线上一点到另一直线的距离。 3、如果两个直线不平行,则可…...
定期检查滚珠丝杆的频率是多久?
定期检查滚珠丝杆的频率通常是每半年进行一次,根据不同的使用环境和设备类型,滚珠丝杆的检查周期有所不同。接下来我们一起看看滚珠丝杆的维护保养方法: 1、清洗:每隔一段时间对滚珠丝杆进行清洁,将滚珠丝杆拆…...
Spark-SQL连接Hive全攻略
在大数据处理领域,Spark-SQL与Hive的结合能发挥强大的功能。今天就来给大家分享一下Spark-SQL连接Hive的多种方式。 Spark SQL编译时可选择包含Hive支持,这样就能使用Hive表访问、UDF、HQL等特性,而且无需提前安装Hive。其连接方式丰富多样…...
在Ubuntu 18.04下编译OpenJDK 11
在Ubuntu 18.04下编译OpenJDK 11 源码下载地址: 链接: https://pan.baidu.com/s/1QAdu-B6n9KqeBakGlpBS3Q 密码: 8lho Linux下的环境要求 不同版本的jdk会要求在不同版本的Ubuntu下编译,不要用太高版本的Ubuntu或者gcc,特别是gcc…...
Spring MVC 一个简单的多文件上传
原始代码逐行解释 PostMapping("/uploads") // ① 声明处理POST请求,路径为"/uploads" ResponseBody // ② 直接返回数据到响应体,不进行视图解析 public String uploads(MultipartFile[] files, // …...
FreeRTos学习记录--1.工程创建与源码概述
1.工程创建与源码概述 1.1 工程创建 使用STM32CubeMX,可以手工添加任务、队列、信号量、互斥锁、定时器等等。但是本课程不想严重依赖STM32CubeMX,所以不会使用STM32CubeMX来添加这些对象,而是手写代码来使用这些对象。 使用STM32CubeMX时&…...
Vmware esxi 给现有磁盘增加空间后并扩展系统里磁盘空间
当前EXSI上虚拟机所在的单独数据磁盘空间满了,需要对空间进行扩容,我们先在主机对磁盘容量进行调整,然后在系统里面对磁盘空间进行拓展,这些操作需要保留数据并且不改变现有的磁盘格局。 遵循大致操作流程是: 1.先登录…...
Linux基础学习--linux的文件权限与目录配置
linux的文件权限与目录配置 1.用户与用户组 在Linux中,每个文件都有相当多的属性和权限,其中最重要的概念就是文件的拥有者。 1.1 文件拥有者 Linux是一个多人多任务的系统,常常有多人共用一台主机的情况出现,因此在系统中可以…...
LLM大模型中的基础数学工具—— 约束优化
Q26: 推导拉格朗日乘子法 的 KKT 条件 拉格朗日乘子法与 KKT 条件是啥? 拉格朗日乘子法是解决约束优化问题的利器。比如,想最小化函数 ,同时满足约束 ,就构造拉格朗日函数 ( 是乘子)。KKT 条件是解这类问…...
涨薪技术|0到1学会性能测试第20课-关联技术
前面的推文我们掌握了性能测试脚本开发参数化技术一系列知识,今天开始给大家分享关联技术知识,后续文章都会系统分享干货,带大家从0到1学会性能测试! 关联是LoadRunner中一个很重要的应用,对于初学者来说也是最容易犯错的地方,但是很遗憾的是,并没有任何特定的错误与关联…...