妙解设计模式之适配器模式
目录
- 适配器模式的概念
- 生活中的例子
- 在编程中的例子
- 软件工程中的实际应用
- 兼容旧接口
- 整合第三方库
- 简化复杂接口
- 跨平台支持
- 总结
适配器模式的概念
适配器模式是一种结构设计模式,它允许将接口不兼容的类通过一个适配器类进行适配,使得这些类可以一起工作。适配器模式通常用于以下情况:
- 当一个接口的实现类已经存在,但是另一个接口需要的是不兼容的时候。
- 当需要将一个类的功能与另一个类一起使用,但是两者之间的接口不一致时。
- 当需要使用一个已经存在的类,但是它的接口不符合当前的需求。
适配器模式包含以下几个组成成员
-
目标接口(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)文本编…...
智能奶柜:重塑牛奶零售新篇章
智能奶柜:重塑牛奶零售新篇章 回忆往昔,孩童时代对送奶员每日拜访的期待,那熟悉的一幕——新鲜牛奶被细心放置于家门口的奶箱中,成为了许多人温馨的童年记忆。如今,尽管直接投递袋装牛奶的情景已不多见,但…...
源代码防泄密--沙盒技术安全风险分析
将原本用于防护病毒木马的沙盒(沙箱)技术,运用于源代码防泄密领域,形成沙盒防泄密系统,是否安全可行?依据沙盒防泄密基本工作原理,可从安全模型、沙箱逃逸以及与进程相关性等多个角度࿰…...
韭菜收割项目
最近在玩股票,被人当成韭菜收割了一顿。高点追涨,第二天直接跌停。以为是低点,想抄底,结果别人直接抄家,血亏!!! 作为一个程序员,还是好好敲代码赚钱好了,一步一步。想不劳而获是不可能的。 我写…...
(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...
Flask RESTful 示例
目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题: 下面创建一个简单的Flask RESTful API示例。首先,我们需要创建环境,安装必要的依赖,然后…...
3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...
全球首个30米分辨率湿地数据集(2000—2022)
数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...
如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...
跨链模式:多链互操作架构与性能扩展方案
跨链模式:多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈:模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展(H2Cross架构): 适配层…...
04-初识css
一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...
回溯算法学习
一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...
动态 Web 开发技术入门篇
一、HTTP 协议核心 1.1 HTTP 基础 协议全称 :HyperText Transfer Protocol(超文本传输协议) 默认端口 :HTTP 使用 80 端口,HTTPS 使用 443 端口。 请求方法 : GET :用于获取资源,…...
