妙解设计模式之适配器模式
目录
- 适配器模式的概念
- 生活中的例子
- 在编程中的例子
- 软件工程中的实际应用
- 兼容旧接口
- 整合第三方库
- 简化复杂接口
- 跨平台支持
- 总结
适配器模式的概念
适配器模式是一种结构设计模式,它允许将接口不兼容的类通过一个适配器类进行适配,使得这些类可以一起工作。适配器模式通常用于以下情况:
- 当一个接口的实现类已经存在,但是另一个接口需要的是不兼容的时候。
- 当需要将一个类的功能与另一个类一起使用,但是两者之间的接口不一致时。
- 当需要使用一个已经存在的类,但是它的接口不符合当前的需求。
适配器模式包含以下几个组成成员
-
目标接口(Target):定义客户端需要的接口,适配器模式的目标对象是实现了目标接口的类。
-
适配器(Adapter):实现了目标接口并包含了被适配对象的引用,通过适配器可以将不兼容的类转换为符合目标接口要求的类。
-
被适配者(Adaptee):需要适配的类,它包含了原本不符合目标接口的方法。
适配器模式有两种实现方式:类适配器模式和对象适配器模式
-
类适配器模式:适配器类
继承自被适配者类,并同时实现了目标接口,通过多重继承将两者结合在一起。 -
对象适配器模式:适配器类持有被适配者类的
实例,并通过委托调用被适配者类的方法来实现目标接口。
适配器模式可以帮助我们在系统中快速整合现有的代码,同时也提高代码的可重用性和解耦性。其核心思想是提供一个中间层,充当翻译器的角色,将不兼容的类转换为可兼容的类。
生活中的例子
想象一下,你有一个老旧的玩具车,它只能用旧式的电池(比如圆柱形的AA电池)。现在你买了一些新型的电池(比如方形的电池),这两个电池形状不一样,不能直接用在玩具车上。这时,你需要一个小工具,让新型电池可以用在老旧的玩具车上,这个小工具就是适配器。
在编程中的例子
在编程中,适配器模式帮助我们把一个类的接口转换成另外一个类可以理解的接口。
假设我们有一个旧的接口叫做OldCharger,它有一个方法chargeWithOldCable(),用来给设备充电。但我们现在有一个新设备,这个设备用的是NewCharger接口,方法是chargeWithNewCable()。
// 旧的充电器接口
class OldCharger {public void chargeWithOldCable() {System.out.println("Charging with old cable");}
}// 新的充电器接口
interface NewCharger {void chargeWithNewCable();
}// 适配器类
class ChargerAdapter implements NewCharger {private OldCharger oldCharger;public ChargerAdapter(OldCharger oldCharger) {this.oldCharger = oldCharger;}@Overridepublic void chargeWithNewCable() {oldCharger.chargeWithOldCable();}
}
在这个例子中:
OldCharger是旧接口。NewCharger是新接口。ChargerAdapter是适配器,它让OldCharger能用NewCharger的接口。
使用适配器的好处
- 兼容性:你可以在不修改旧代码的情况下使用新功能。
- 灵活性:可以很容易地切换不同的接口。
软件工程中的实际应用
兼容旧接口
在大型项目中,旧的系统或接口可能会存在很长时间,而新的功能开发需要与这些旧接口兼容。通过适配器模式,可以在不修改旧代码的情况下使新代码与旧接口兼容。
示例:假设我们有一个旧的支付系统OldPaymentService,它提供的方法是processOldPayment(String details)。新的支付系统NewPaymentService使用processNewPayment(PaymentInfo paymentInfo)。
// 旧的支付系统
class OldPaymentService {public void processOldPayment(String details) {// 处理旧支付}
}// 新的支付信息类
class PaymentInfo {private String cardNumber;private String expiryDate;private double amount;// getters and setters
}// 新的支付系统接口
interface NewPaymentService {void processNewPayment(PaymentInfo paymentInfo);
}// 适配器类
class PaymentAdapter implements NewPaymentService {private OldPaymentService oldPaymentService;public PaymentAdapter(OldPaymentService oldPaymentService) {this.oldPaymentService = oldPaymentService;}@Overridepublic void processNewPayment(PaymentInfo paymentInfo) {String details = paymentInfo.getCardNumber() + ":" + paymentInfo.getExpiryDate() + ":" + paymentInfo.getAmount();oldPaymentService.processOldPayment(details);}
}
整合第三方库
在项目中,我们可能需要整合不同的第三方库,这些库的接口不统一。通过适配器模式,可以将第三方库的接口统一起来,方便项目中使用。
示例:假设我们有两个不同的日志库LibraryALogger和LibraryBLogger,我们希望统一使用一个Logger接口。
// 第一个日志库
class LibraryALogger {public void logMessage(String msg) {System.out.println("LibraryA: " + msg);}
}// 第二个日志库
class LibraryBLogger {public void writeLog(String msg) {System.out.println("LibraryB: " + msg);}
}// 日志接口
interface Logger {void log(String message);
}// 第一个日志库的适配器
class LibraryALoggerAdapter implements Logger {private LibraryALogger logger;public LibraryALoggerAdapter(LibraryALogger logger) {this.logger = logger;}@Overridepublic void log(String message) {logger.logMessage(message);}
}// 第二个日志库的适配器
class LibraryBLoggerAdapter implements Logger {private LibraryBLogger logger;public LibraryBLoggerAdapter(LibraryBLogger logger) {this.logger = logger;}@Overridepublic void log(String message) {logger.writeLog(message);}
}
简化复杂接口
在某些情况下,我们可能需要简化一个复杂接口,以便在项目中更容易使用。适配器模式可以提供一个简化的接口来包装复杂的类。
示例:假设我们有一个复杂的图形库ComplexGraphicsLibrary,它有许多复杂的方法。我们可以使用适配器模式简化它的接口。
// 复杂的图形库
class ComplexGraphicsLibrary {public void drawLine(int x1, int y1, int x2, int y2) {// 画线逻辑}public void drawCircle(int x, int y, int radius) {// 画圆逻辑}
}// 简化的图形接口
interface SimpleGraphics {void drawShape(String shapeType, int... params);
}// 适配器类
class SimpleGraphicsAdapter implements SimpleGraphics {private ComplexGraphicsLibrary graphicsLibrary;public SimpleGraphicsAdapter(ComplexGraphicsLibrary graphicsLibrary) {this.graphicsLibrary = graphicsLibrary;}@Overridepublic void drawShape(String shapeType, int... params) {if (shapeType.equalsIgnoreCase("line")) {graphicsLibrary.drawLine(params[0], params[1], params[2], params[3]);} else if (shapeType.equalsIgnoreCase("circle")) {graphicsLibrary.drawCircle(params[0], params[1], params[2]);}}
}
跨平台支持
// 桌面平台存储接口
class DesktopStorage {public void saveFile(String path, String data) {// 保存文件到桌面平台}
}// 移动平台存储接口
class MobileStorage {public void saveToPath(String path, String data) {// 保存文件到移动平台}
}// 统一的存储接口
interface Storage {void save(String path, String data);
}// 桌面平台存储的适配器
class DesktopStorageAdapter implements Storage {private DesktopStorage storage;public DesktopStorageAdapter(DesktopStorage storage) {this.storage = storage;}@Overridepublic void save(String path, String data) {storage.saveFile(path, data);}
}// 移动平台存储的适配器
class MobileStorageAdapter implements Storage {private MobileStorage storage;public MobileStorageAdapter(MobileStorage storage) {this.storage = storage;}@Overridepublic void save(String path, String data) {storage.saveToPath(path, data);}
}
总结
适配器模式是一种结构型设计模式,用于将一个类的接口转换成客户端期望的另一个接口,使得接口不兼容的类可以一起工作。它的主要应用场景包括兼容旧接口、整合第三方库、简化复杂接口和跨平台支持。适配器模式可以提高代码的可维护性和扩展性,减少不同系统之间的耦合。
相关文章:
妙解设计模式之适配器模式
目录 适配器模式的概念生活中的例子在编程中的例子 软件工程中的实际应用兼容旧接口整合第三方库简化复杂接口跨平台支持 总结 适配器模式的概念 适配器模式是一种结构设计模式,它允许将接口不兼容的类通过一个适配器类进行适配,使得这些类可以一起工作…...
【Linux】Linux下centos更换国内yum源
🌱博客主页:青竹雾色间 🌱系列专栏:Linux 😘博客制作不易欢迎各位👍点赞⭐收藏➕关注 目录 1. 备份旧的 YUM 源文件2. 下载国内的 YUM 源文件阿里云:网易: 3. 清理 YUM 缓存4. 更新…...
HTML静态网页成品作业(HTML+CSS)——动漫熊出没介绍网页(3个页面)
🎉不定期分享源码,关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 🏷️本套采用HTMLCSS,未使用Javacsript代码,共有3个页面。 二、作品演示 三、代…...
算法训练营day42
dp含义确定 递推公式 初始化 遍历顺序 打印 题目1:62. 不同路径 - 力扣(LeetCode) class Solution { public:int uniquePaths(int m, int n) {// 定义dp数组 含义是每个坐标到达的路径vector<vector<int>> dp(m, vector&l…...
【Vue】自动导入组件
1. 下载插件 npm install unplugin-vue-components 2. 修改vite.config.js import { fileURLToPath, URL } from node:urlimport { defineConfig } from vite import vue from vitejs/plugin-vue import Components from unplugin-vue-components/vite // 按需加载自定义组件/…...
mfc140u.dll丢失的解决方法有哪些?怎么全面修复mfc140u.dll文件
mfc140u.dll丢失其实相对来说不太常见到,因为这个文件一般是不丢失的,不过既然有人遇到这种问题,那么小编一定满足各位,给大家详细的唠叨一下mfc140u.dll丢失的各种解决方法,教大家以最快最有效率的方法去解决mfc140u.…...
MySQL8报错Public Key Retrieval is not allowedz 怎么解决?
问题描述 当我们使用数据库管理工具连接mysql8的时候,可能遇到报错: Public Key Retrieval is not allowed 解决办法 1、在连接属性中配置allowPublicKeyRetrieval设置为true 2、在连接URL中加上配置allowPublicKeyRetrieval为true...
海外动态IP代理如何提高效率?
动态住宅IP代理之所以能够有效提升数据爬取的效率和准确性,主要归功于其提供的IP地址具有高度的匿名性和真实性。这些IP地址来自于真实的用户网络,因此相比于数据中心IP,它们更不容易被网站的安全系统标识为爬虫。此外,由于IP地址…...
解析边缘计算网关的优势-天拓四方
随着信息化、智能化浪潮的持续推进,计算技术正以前所未有的速度发展,而边缘计算网关作为其中的重要一环,以其独特的优势正在逐步改变我们的生活方式和工作模式。本文将详细解析边缘计算网关的优势。 首先,边缘计算网关具有显著的…...
计算机原理 知识回顾
第一部分:计算机基础概念 计算机的定义 计算机的演化历程计算机的分类(超级计算机、桌面计算机、便携式计算机等) 计算机的基本组成 输入设备、输出设备中央处理单元(CPU)、存储器、主板 计算机的工作原理 数据输…...
WPF 如何调试
简述 它是一种系统机制,用于识别和修复一段代码中的错误或缺陷,这些错误或缺陷的行为与您的预期不同。调试子系统紧密耦合的复杂应用程序并不容易,因为修复一个子系统中的错误可能会在另一个子系统中创建错误。 在 C# 中调试 在 WPF 应用程序…...
URL跳转
1.URL介绍 开放重定向(Open Redirect),也叫URL跳转漏洞,是指服务端未对传入的跳转url变量进行检查和控制,导致诱导用户跳转到恶意网站,由于是从可信的站点跳转出去的,用户会比较信任。 2.URL跳…...
Spring Boot集成rss快速入门demo
1.什么是rss? RSS 的全称是「简易内容聚合」(Really Simple Syndication),是一个能让你在一个地方订阅各种感兴趣网站的工具。 一个网站支持 RSS,就意味着每当它新发布一篇新文章,就会往一个位于特定网址的…...
重学java 49 List接口
但逢良辰,顺颂时宜 —— 24.5.28 一、List接口 1.概述: 是collection接口的子接口 2.常见的实现类: ArrayList LinkedList Vector 二、List集合下的实现类 1.ArrayList集合的使用及源码分析 1.概述 ArrayList是List接口的实现类 2.特点 a.元素有序 —> 按照什么顺…...
【html+css(大作业)】二级菜单导航栏
目录 实现效果 代码及其解释 html部分 CSS部分 hello,hello好久不见! 今天我们来写二级导航栏,所谓二级导航栏,简单来说就是鼠标放上去就有菜单拉出: 实现效果 代码及其解释 html部分 <!DOCTYPE html> &l…...
算法基础之集合-Nim游戏
集合-Nim游戏 核心思想: 博弈论 sg函数:在有向图游戏中,对于每个节点x,设从x出发共有k条有向边,分别到达节点y1,y2,yk,定义SG(x)的后记节点y1,y2,,yk的SG函数值构成的集合在执行mex运算的结果,即:SG(x)mex({SG(y1),SG(y2)SG(yk)}) **特别地,**整个有向图…...
Diffusion Model, Stable Diffusion, Stable Diffusion XL 详解
文章目录 Diffusion Model生成模型DDPM概述向前扩散过程前向扩散的逐步过程前向扩散的整体过程 反向去噪过程网络结构训练和推理过程训练过程推理过程优化目标 详细数学推导数学基础向前扩散过程反向去噪过程 Stable Diffusion组成结构运行流程网络结构变分自编码器 (VAE)文本编…...
智能奶柜:重塑牛奶零售新篇章
智能奶柜:重塑牛奶零售新篇章 回忆往昔,孩童时代对送奶员每日拜访的期待,那熟悉的一幕——新鲜牛奶被细心放置于家门口的奶箱中,成为了许多人温馨的童年记忆。如今,尽管直接投递袋装牛奶的情景已不多见,但…...
源代码防泄密--沙盒技术安全风险分析
将原本用于防护病毒木马的沙盒(沙箱)技术,运用于源代码防泄密领域,形成沙盒防泄密系统,是否安全可行?依据沙盒防泄密基本工作原理,可从安全模型、沙箱逃逸以及与进程相关性等多个角度࿰…...
韭菜收割项目
最近在玩股票,被人当成韭菜收割了一顿。高点追涨,第二天直接跌停。以为是低点,想抄底,结果别人直接抄家,血亏!!! 作为一个程序员,还是好好敲代码赚钱好了,一步一步。想不劳而获是不可能的。 我写…...
网络六边形受到攻击
大家读完觉得有帮助记得关注和点赞!!! 抽象 现代智能交通系统 (ITS) 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 (…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)
HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...
AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...
Cinnamon修改面板小工具图标
Cinnamon开始菜单-CSDN博客 设置模块都是做好的,比GNOME简单得多! 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...
QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...
使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
DingDing机器人群消息推送
文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人,点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置,详见说明文档 成功后,记录Webhook 2 API文档说明 点击设置说明 查看自…...
uniapp 开发ios, xcode 提交app store connect 和 testflight内测
uniapp 中配置 配置manifest 文档:manifest.json 应用配置 | uni-app官网 hbuilderx中本地打包 下载IOS最新SDK 开发环境 | uni小程序SDK hbulderx 版本号:4.66 对应的sdk版本 4.66 两者必须一致 本地打包的资源导入到SDK 导入资源 | uni小程序SDK …...
【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)
LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 题目描述解题思路Java代码 题目描述 题目链接:LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...
