深入理解设计模式之适配器模式
深入理解设计模式之适配器模式
1. 适配器模式概述
适配器模式(Adapter Pattern)是一种结构型设计模式,它允许将一个类的接口转换为客户端所期望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的类能够协同工作,扮演了"转换器"的角色。
2. 适配器模式的核心组成
- 目标接口(Target): 客户端所期望的接口
- 适配者(Adaptee): 需要被适配的类或接口
- 适配器(Adapter): 将适配者接口转换为目标接口的类
3. 适配器模式的类型
3.1 对象适配器
通过组合方式实现,适配器持有适配者的实例。
3.2 类适配器
通过继承方式实现,适配器同时继承适配者和实现目标接口。
4. 适配器模式实现
4.1 对象适配器实现
// 目标接口
interface MediaPlayer {void play(String audioType, String fileName);
}// 适配者接口
interface AdvancedMediaPlayer {void playVlc(String fileName);void playMp4(String fileName);
}// 具体适配者实现
class VlcPlayer implements AdvancedMediaPlayer {@Overridepublic void playVlc(String fileName) {System.out.println("Playing vlc file: " + fileName);}@Overridepublic void playMp4(String fileName) {// 不做任何事}
}class Mp4Player implements AdvancedMediaPlayer {@Overridepublic void playVlc(String fileName) {// 不做任何事}@Overridepublic void playMp4(String fileName) {System.out.println("Playing mp4 file: " + fileName);}
}// 适配器
class MediaAdapter implements MediaPlayer {private AdvancedMediaPlayer advancedMusicPlayer;public MediaAdapter(String audioType) {if (audioType.equalsIgnoreCase("vlc")) {advancedMusicPlayer = new VlcPlayer();} else if (audioType.equalsIgnoreCase("mp4")) {advancedMusicPlayer = new Mp4Player();}}@Overridepublic void play(String audioType, String fileName) {if (audioType.equalsIgnoreCase("vlc")) {advancedMusicPlayer.playVlc(fileName);} else if (audioType.equalsIgnoreCase("mp4")) {advancedMusicPlayer.playMp4(fileName);}}
}// 客户端
class AudioPlayer implements MediaPlayer {private MediaAdapter mediaAdapter;@Overridepublic void play(String audioType, String fileName) {// 播放mp3格式音频文件的内置支持if (audioType.equalsIgnoreCase("mp3")) {System.out.println("Playing mp3 file: " + fileName);}// 通过适配器播放其他格式else if (audioType.equalsIgnoreCase("vlc") || audioType.equalsIgnoreCase("mp4")) {mediaAdapter = new MediaAdapter(audioType);mediaAdapter.play(audioType, fileName);} else {System.out.println("Invalid media. " + audioType + " format not supported");}}
}// 测试类
public class AdapterPatternDemo {public static void main(String[] args) {AudioPlayer audioPlayer = new AudioPlayer();audioPlayer.play("mp3", "beyond_the_horizon.mp3");audioPlayer.play("mp4", "alone.mp4");audioPlayer.play("vlc", "far_far_away.vlc");audioPlayer.play("avi", "mind_me.avi");}
}
4.2 类适配器实现
// 目标接口
interface Target {void request();
}// 适配者类
class Adaptee {public void specificRequest() {System.out.println("Specific request from Adaptee");}
}// 类适配器
class ClassAdapter extends Adaptee implements Target {@Overridepublic void request() {specificRequest(); // 调用父类方法}
}// 测试类
public class ClassAdapterDemo {public static void main(String[] args) {Target target = new ClassAdapter();target.request();}
}
5. 真实世界适配器模式示例
5.1 旧系统集成案例
// 旧支付系统接口
class LegacyPaymentSystem {public void processPayment(String amount, String account) {System.out.println("Legacy payment processed: " + amount + " from account: " + account);}
}// 新支付接口
interface ModernPaymentGateway {void processPayment(Payment payment);
}// 支付数据传输对象
class Payment {private double amount;private String accountId;private String currency;public Payment(double amount, String accountId, String currency) {this.amount = amount;this.accountId = accountId;this.currency = currency;}public double getAmount() {return amount;}public String getAccountId() {return accountId;}public String getCurrency() {return currency;}
}// 支付系统适配器
class PaymentSystemAdapter implements ModernPaymentGateway {private LegacyPaymentSystem legacySystem;public PaymentSystemAdapter() {this.legacySystem = new LegacyPaymentSystem();}@Overridepublic void processPayment(Payment payment) {// 转换和适配String amount = payment.getAmount() + " " + payment.getCurrency();legacySystem.processPayment(amount, payment.getAccountId());}
}// 客户端代码
public class PaymentSystemDemo {public static void main(String[] args) {// 创建新的支付请求Payment payment = new Payment(100.50, "ACC123456", "USD");// 使用适配器处理支付ModernPaymentGateway paymentGateway = new PaymentSystemAdapter();paymentGateway.processPayment(payment);}
}
5.2 第三方库集成案例
// 第三方图表库接口
class ThirdPartyChartLibrary {public void displayChart(String data, String type, int width, int height) {System.out.println("Displaying " + type + " chart with data: " + data);System.out.println("Dimensions: " + width + "x" + height);}
}// 应用程序预期的图表接口
interface AppChartInterface {void drawChart(ChartData data);
}// 图表数据
class ChartData {private String dataPoints;private ChartType type;private Dimension size;public ChartData(String dataPoints, ChartType type, Dimension size) {this.dataPoints = dataPoints;this.type = type;this.size = size;}public String getDataPoints() {return dataPoints;}public ChartType getType() {return type;}public Dimension getSize() {return size;}
}// 图表类型枚举
enum ChartType {BAR, LINE, PIE
}// 尺寸类
class Dimension {private int width;private int height;public Dimension(int width, int height) {this.width = width;this.height = height;}public int getWidth() {return width;}public int getHeight() {return height;}
}// 图表库适配器
class ChartLibraryAdapter implements AppChartInterface {private ThirdPartyChartLibrary chartLibrary;public ChartLibraryAdapter() {this.chartLibrary = new ThirdPartyChartLibrary();}@Overridepublic void drawChart(ChartData data) {// 转换数据格式String chartType = data.getType().toString().toLowerCase();chartLibrary.displayChart(data.getDataPoints(),chartType,data.getSize().getWidth(),data.getSize().getHeight());}
}// 客户端代码
public class ChartAdapterDemo {public static void main(String[] args) {// 创建图表数据ChartData barChartData = new ChartData("10,25,30,40,50",ChartType.BAR,new Dimension(800, 400));// 使用适配器绘制图表AppChartInterface chartInterface = new ChartLibraryAdapter();chartInterface.drawChart(barChartData);}
}
6. 双向适配器模式
// 接口A
interface SystemA {void operationA();
}// 接口B
interface SystemB {void operationB();
}// 系统A实现
class ConcreteSystemA implements SystemA {@Overridepublic void operationA() {System.out.println("System A: performing operation A");}
}// 系统B实现
class ConcreteSystemB implements SystemB {@Overridepublic void operationB() {System.out.println("System B: performing operation B");}
}// 双向适配器
class TwoWayAdapter implements SystemA, SystemB {private SystemA systemA;private SystemB systemB;public TwoWayAdapter(SystemA systemA, SystemB systemB) {this.systemA = systemA;this.systemB = systemB;}@Overridepublic void operationA() {System.out.println("Adapter: redirecting operationA to operationB");systemB.operationB();}@Overridepublic void operationB() {System.out.println("Adapter: redirecting operationB to operationA");systemA.operationA();}
}// 测试双向适配器
public class TwoWayAdapterDemo {public static void main(String[] args) {SystemA systemA = new ConcreteSystemA();SystemB systemB = new ConcreteSystemB();// 创建双向适配器TwoWayAdapter adapter = new TwoWayAdapter(systemA, systemB);// 通过适配器调用SystemA的功能System.out.println("Client uses SystemA interface:");adapter.operationA();// 通过适配器调用SystemB的功能System.out.println("\nClient uses SystemB interface:");adapter.operationB();}
}
7. Java标准库中的适配器模式
Java标准库中有多处使用了适配器模式:
import java.io.*;
import java.util.*;public class JavaAdapterExamples {public static void main(String[] args) {// 示例1: InputStreamReader作为适配器// InputStream(适配者) -> Reader(目标接口)try {InputStream is = new FileInputStream("file.txt");Reader reader = new InputStreamReader(is, "UTF-8");int data = reader.read();while(data != -1) {System.out.print((char) data);data = reader.read();}reader.close();} catch (Exception e) {e.printStackTrace();}// 示例2: Arrays.asList()作为适配器// 数组(适配者) -> List(目标接口)String[] namesArray = {"John", "Jane", "Adam"};List<String> namesList = Arrays.asList(namesArray);// 使用List接口的方法System.out.println(namesList.get(1));System.out.println(namesList.contains("Jane"));// 示例3: Collections.enumeration()作为适配器// Collection(适配者) -> Enumeration(目标接口)List<String> list = new ArrayList<>();list.add("Item 1");list.add("Item 2");Enumeration<String> enumeration = Collections.enumeration(list);while(enumeration.hasMoreElements()) {System.out.println(enumeration.nextElement());}}
}
8. 适配器模式的优缺点
优点
- 解耦合: 客户端与被适配者解耦,通过目标接口进行交互
- 复用性: 可以复用现有的类,即使接口不匹配
- 灵活性: 可以在不修改原有代码的情况下使用已有功能
- 开闭原则: 符合"开闭原则",不修改原有代码而扩展功能
缺点
- 复杂性: 引入额外的类,增加系统复杂度
- 性能开销: 可能引入额外的间接调用,影响性能
- 可读性: 过多的适配器会使系统难以理解
9. 适配器模式的适用场景
- 集成遗留系统: 当需要集成旧系统或第三方库时
- 接口不兼容: 当现有接口与客户端期望的接口不兼容时
- 类库迁移: 当迁移到新的类库但不想修改现有代码时
- 多态接口: 当需要统一多个类的接口时
10. 适配器模式与其他模式的比较
模式 | 适配器模式 | 桥接模式 | 装饰器模式 | 代理模式 |
---|---|---|---|---|
意图 | 使不兼容接口兼容 | 将抽象与实现分离 | 动态添加功能 | 控制对象访问 |
结构 | 转换接口 | 分离层次结构 | 包装对象 | 包装对象 |
侧重点 | 接口转换 | 接口与实现分离 | 功能扩展 | 访问控制 |
11. 总结
适配器模式是一种强大的结构型设计模式,用于解决接口不兼容问题。它在系统集成、类库迁移和接口统一等场景中非常有用。通过将一个类的接口转换为客户端期望的另一个接口,适配器让原本不兼容的类能够协同工作。
适配器模式的两种主要实现方式——对象适配器和类适配器,为不同场景提供了灵活的解决方案。在实际开发中,对象适配器因其更高的灵活性和更低的耦合度而被更广泛使用。
适当使用适配器模式可以大大提高代码的可维护性和可扩展性,特别是在处理遗留系统和第三方库集成时。但也应注意避免过度使用适配器,以免增加系统的复杂性。
相关文章:
深入理解设计模式之适配器模式
深入理解设计模式之适配器模式 1. 适配器模式概述 适配器模式(Adapter Pattern)是一种结构型设计模式,它允许将一个类的接口转换为客户端所期望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的类能够协同工作,扮演了"转换器&quo…...
预训练模型:深度学习的通用特征引擎
预训练模型是深度学习领域的重要技术,其核心思想是通过大规模数据预先学习通用特征,再迁移到具体任务中进行微调。以下是其定义、原理及与其他模型的对比分析: 一、预训练模型的定义与原理 基本概念 预训练模型(Pre-trained Model…...
C++题解(33)2025年顺德区中小学生程序设计展示活动(初中组C++)U560876 美丽数(一)和 U560878 美丽数(二)题解
U560876 美丽数(一) 题目描述 小明很喜欢3和5这两个数字,他将能被3或5整除的数叫做美丽数。现在给你一个整数n,你能告诉小明第n个美丽数是多少吗? 输入格式 输入有多行,每行只有一个整数${n_i}$。 输出格式…...

产业互联网+三融战略:重构企业增长密码
产业互联网时代:用"三融"重构企业增长飞轮 在产业互联网浪潮下,企业面临资源分散、资金短缺、人才难聚的三重挑战。本文提出的"融人、融资、融资源"顶层设计,正为新时代企业构建增长新引擎。 一、三级合伙人体系&#x…...
centos yum源,docker源
yum源repo文件: wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repodocker源repo文件: yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo安装docker和docker c…...
通过设备节点获取已注册的 i2c client
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言代码分析 前言 另一个驱动通过设备节点 获取已注册的i2c client 代码分析 #include <linux/kernel.h> #include <linux/init.h> #include <li…...

Centos系统资源镜像配置
主要体现 yum 命令执行报错,排除网络连接问题 解决步骤: 下载安装工具 # 安装 wget curl vim yum install -y wget curl vim 原有repo文件备份 # 进入配置文件所在文件夹 cd /etc/yum.repos.d# 创建 backup 文件夹 mkdir backup# 备份文件放置文件夹 m…...

【Linux网络篇】:Socket网络套接字以及简单的UDP网络程序编写
✨感谢您阅读本篇文章,文章内容是个人学习笔记的整理,如果哪里有误的话还请您指正噢✨ ✨ 个人主页:余辉zmh–CSDN博客 ✨ 文章所属专栏:Linux篇–CSDN博客 文章目录 网络编程套接字一.预备知识1.理解源IP地址和目的IP地址2.认识端…...

学习路之uniapp--unipush2.0推送功能--给自己发通知
学习路之uniapp--unipush2.0推送功能--给自己发通知 一、绑定云空间及创建云函数二、编写发送界面三、效果后期展望: 一、绑定云空间及创建云函数 package.json {"name": "server-push","dependencies": {},"main": "…...
Java面向对象 一
系列文章目录 Java面向对象 二-CSDN博客 目录 系列文章目录 前言 一、初步认识面向对象 1.类和对象的简单理解 2.类的构成 二、类的实例化 1.对象的创建 2.对象的初始化 三、this引用的作用 四、构造方法 1.构造方法的提供 2.对象的构造 3.构造方法的重载 4.th…...
怎么开发一个网络协议模块(C语言框架)之(二) 数据结构设计
一、数据结构设计模板分析 (gdb) p gVrrpInstance $3 = { INT4 socketV4 = 107, .... vrrpStatisticsEntry_t SvrrpStatistics = {delIp4Count = 0, delIp6Count = 0, delIp4Error = 0, delIp6Error = 0, addIp4Count = 0, addIp6Count = 3, addIp4Error = 0, addIp6Error …...
30天自制操作系统day5(vram和显存)(GDT和IDT)(c语言结构体)(汇编-c)(ai辅助整理)
day5 harib02d c语言结构体的一些解释 struct BOOTINFO { char cyls, leds, vmode, reserve; short scrnx, scrny; char *vram; }; //最开始的struct命令只是把一串变量声明集中起来,统一叫做“struct BOOTINFO”。 //最初是1字节的变量cyls,接着是1字…...
【音频】drc 限幅器、多带限幅器、压缩器、多带压缩器
以下是关于 DRC 限幅器、多带限幅器、压缩器、多带压缩器的详细解释,它们均为音频处理领域的动态范围控制设备,主要用于调整音频信号的动态范围(即最大音量与最小音量的差值),以优化音质或满足特定播放需求: 一、DRC 限幅器(Dynamic Range Compression Limiter) 核心功…...

leetcode hot100刷题日记——12.反转链表
解答: /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val(x), next(nullptr) {}* ListNode(int x, ListNode *next) : val(x), next(n…...
osgEarth中视角由跟随模式切换到漫游模式后没有鼠标拖拽功能问题分析及解决方法
遇到了一个棘手的问题,就是在由跟随模式切换到漫游模式的时候,鼠标无法实现拖拽功能。后来发现是前面给自己挖的坑。 因为要实现鼠标点选某个模型后,模型需要变红色显示,所以添加了一个事件处理程序。 // 创建 场景中模型的点选功能 事件处理程序 ModelSelectionHandler* …...
STM32中断优先级分组有哪几种?
STM32中断优先级分组主要有以下5种: 分组0:所有16位用于子优先级,没有抢占优先级。此时可配置的子优先级为0~15,共16级,适用于系统中对中断实时性要求不高,且中断源较多,需要更多子优先级来区分不同中断的情况。分组1:最高1位用于抢占优先级,最低3位用于子优先级。可配…...

《Python语言程序设计》第4章第8题3个个位数之间比大小。‘a小于b而b大于c’这是最有漏洞的一个对比,请问我如何判断a和c
升序来做这个题 比如123就变成321 需要比对3个数 这不是比对2个数。a和b比对 我们可以直接写 if a>b: print(ab) else print(ba) 但是现在是3个数abc 如果进行if比对呢 if a > b >c: print(a,b,c) elif a < b >c: print(bca) … 简洁的代码变成了复杂的代码段。…...

Selenium 测试框架 - Python
🚀Selenium Python 实战指南:从入门到进阶 Selenium 是 Web 自动化测试中最受欢迎的工具之一,支持多种浏览器和语言。本文将从环境搭建到多浏览器兼容、测试框架集成、元素定位方式、常用操作、浏览器配置等多个方面进行详细讲解,并分享常见的最佳实践建议。 📦一、环境…...

RNN GRU LSTM 模型理解
一、RNN 1. 在RNN中, 二、GRU 1. GRU是为了解决RNN 梯度消失引入的改良模型, 2. GRU 通过门控 Gamma_r Gamma_u 两个变量,实现了对于过往记忆的筛选:这种机制使得GRU能够灵活地决定何时“忘记”过去的信息以及何时“记住”新的…...
AutoCompose - 携程自动编排原理 -【编排关系DAG的构建】
AutoCompose - 携程自动编排原理 -【编排关系DAG的构建】 前言一. Spring / SpringBoot 的兼容✅ spring.factories 文件🧩 特点📄 示例 ✅ META-INF/spring/ 目录下的文件(Spring Boot 2.4 新特性)🧩 特点Ὄ…...

【MC】红石比较器
在《我的世界》(Minecraft)中,红石比较器(Redstone Comparator) 是一种高级红石元件,主要用于 检测、比较或处理信号强度,同时还能与容器、特定方块互动。 红石比较器有两种模式: 比…...
危化品经营单位安全生产管理人员考试主要内容
危化品经营单位安全生产人员考试主要测试从业人员对危险化学品安全管理的专业知识和法规掌握程度。考试内容涵盖以下重点: 法律法规(30%) 重点考查《安全生产法》《危险化学品安全管理条例》等核心法规,以及经营许可、重大危险源…...
get_the_category() 和 get_the_terms() 的区别
get_the_category() 和 get_the_terms() 是WordPress中用于获取文章分类的两个函数,但它们之间存在一些关键差异: get_the_category() 特定于分类:get_the_category() 函数专门用于获取文章的分类(category)。它返回一个包含所有分类对象的…...

红黑树简单模拟实现
定义成员变量旋转insert以234树的角度来待插入操作具体代码 完整代码 我们前面实现了 二叉搜索树和 AVL树。 其中AVL树是二叉搜索树的改进,但是有些人觉得二叉树搜索的插入调整太频繁了,或者说平衡条件过于苛刻。 于是人们放松了左右子树高度差的限制&…...

豪越科技:消防应急装备智能仓储管理新变革
在消防救援工作中,消防装备无疑是消防员们与火灾等灾害顽强对抗的关键“武器”。然而,传统的消防装备管理模式长期以来饱受诸多痛点的困扰,严重影响着消防工作的高效开展和救援效果。 在过去,装备丢失的情况时有发生。由于缺乏有效…...

如何设计Agent的记忆系统
最近看了一张画Agent记忆分类的图 我觉得分类分的还可以,但是太浅了,于是就着它的逻辑,仔细得写了一下在不同的记忆层,该如何设计和选型 先从流程,作用,实力和持续时间的这4个维度来解释一下这几种记忆&am…...

毕业论文格式(Word)
目录 Word目录怎么自动生成?快速生成试试这3个方法! - 知乎https://zhuanlan.zhihu.com/p/692056836目录生成需要先设置标题样式,这个不仅是目录生成需要,和后续的图表也有关系。 最好不要自己创建新的样式,而是在现有…...

学习STC51单片机14(芯片为STC89C52RC)
接下来我们进入学会了HC—SR04 还有舵机那么现在我们将他们融合在一起,用超声波来引导舵机的转动 我们这个最后的成果是做一个智能垃圾桶 成品是这样的,是不是可有意思了 成品视频 现在我们将舵机的代码和超声波测距模块的代码整合到一起,实…...

基于CodeBuddy实现本地网速的实时浏览小工具
本文所使用的 CodeBuddy 免费下载链接:腾讯云代码助手 CodeBuddy - AI 时代的智能编程伙伴 前言 在数字化浪潮席卷全球的今天,网络已成为人们生活和工作中不可或缺的基础设施。无论是在线办公、学习、娱乐,还是进行大数据传输和云计算&…...

stable diffusion论文解读
High-Resolution Image Synthesis with Latent Diffusion Models 论文背景 LDM是Stable Diffusion模型的奠基性论文 于2022年6月在CVPR上发表 传统生成模型具有局限性: 扩散模型(DM)通过逐步去噪生成图像,质量优于GAN&#x…...