深入理解设计模式之适配器模式

深入理解设计模式之适配器模式

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. 总结

适配器模式是一种强大的结构型设计模式,用于解决接口不兼容问题。它在系统集成、类库迁移和接口统一等场景中非常有用。通过将一个类的接口转换为客户端期望的另一个接口,适配器让原本不兼容的类能够协同工作。

适配器模式的两种主要实现方式——对象适配器和类适配器,为不同场景提供了灵活的解决方案。在实际开发中,对象适配器因其更高的灵活性和更低的耦合度而被更广泛使用。

适当使用适配器模式可以大大提高代码的可维护性和可扩展性,特别是在处理遗留系统和第三方库集成时。但也应注意避免过度使用适配器,以免增加系统的复杂性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/pingmian/82148.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【数据结构 · 初阶】- 快速排序

目录 一. Hoare 版本 1. 单趟 2. 整体 3. 时间复杂度 4. 优化&#xff08;抢救一下&#xff09; 4.1 随机选 key 4.2 三数取中 二. 挖坑法 格式优化 三. 前后指针&#xff08;最好&#xff09; 四. 小区间优化 五. 改非递归 快速排序是 Hoare 提出的一种基于二叉树…

第2周 PINN核心技术揭秘: 如何用神经网络求解偏微分方程

1. PDEs与传统数值方法回顾 (Review of PDEs & Traditional Numerical Methods) 1.1 什么是偏微分方程 (Partial Differential Equations, PDEs)? 偏微分方程是描述自然界和工程领域中各种物理现象(如热量传播、流体流动、波的振动、电磁场分布等)的基本数学语言。 1.…

Neo4j(二) - 使用Cypher操作Neo4j

文章目录 前言一、Cypher简介二、数据库操作1. 创建数据库2. 查看数据库3. 删除数据库4. 切换数据库 三、节点、关系及属性操作1. 创建节点与关系1.1 语法1.2 示例 2. 查询数据2.1 语法2.2 示例 3. 更新数据3.1 语法3.2 示例 4. 删除节点与关系4.1 语法4.2 示例 5. 合并数据5.1…

RabbitMQ的Web管理页面给我看懵了,这都什么意思啊

文章目录 OverviewTotalsMessage RatesQueued Messages NodesChurn StatisticsPorts and ContextsExport DefinitionsImport Definitions ConnectionsChannelsExchangesQueuesAdmin他们之间的关联 在上一篇文章中我们讲到了如何在Windows中安装Rabbitmq&#xff0c; 小白也能搞…

安全基础与协议分析

5.1 Web安全基础 5.1.1 Web安全基础概览&#xff08;一、二&#xff09; Web安全的核心目标是保护Web应用及其数据免受攻击&#xff0c;涵盖以下关键领域&#xff1a; 攻击面&#xff1a; 前端漏洞&#xff08;XSS、CSRF&#xff09;。 后端漏洞&#xff08;SQL注入、RCE&a…

STM32项目实战:ADC采集

STM32F103C8T6的ADC配置。PB0对应的是ADC1的通道8。在标准库中&#xff0c;需要初始化ADC&#xff0c;设置通道&#xff0c;时钟&#xff0c;转换模式等。需要配置GPIOB的第0脚为模拟输入模式&#xff0c;然后配置ADC1的通道8&#xff0c;设置转换周期和触发方式。 接下来是I2C…

第十四章:数据治理之数据源:数据源的数据接入、业务属性梳理及监控

本章开始&#xff0c;将进入9大模块的介绍。第一个模块我们先介绍&#xff1a;数据源。数据源是整个数据中台数据的来源&#xff0c;是一个起点。更好的管理好数据源这个起点&#xff0c;是数据治理的一个好的开始。 在【数据&#xff1a;业务生数据&#xff0c;数据生“万物”…

【C/C++】多线程开发:wait、sleep、yield全解析

文章目录 多线程开发&#xff1a;wait、sleep、yield全解析1 What简要介绍详细介绍wait() — 条件等待&#xff08;用于线程同步&#xff09;sleep() — 睡觉&#xff0c;定时挂起yield() — 自愿让出 CPU 2 区别以及建议区别应用场景建议 3 三者协作使用示例 多线程开发&#…

阿里云CDN刷新预热--刷新URL

文章目录 一、全英文URL刷新预热二、掺杂中文的URL刷新预热2.1 对带中文URL进行编码2.2 预热刷新 三、CDN刷新-核心作用与价值3.1 核心作用3.2 核心价值3.3 典型使用场景 *最后我想说&#xff1a;请你不要相信我说的每一句话&#xff0c;这只是我的个人经验* 一、全英文URL刷新…

Oracle 19c DG备库报错ORA-00313、ORA-00312、ORA-27037

Oracle 19c DG备库报错ORA-00313、ORA-00312、ORA-27037 错误排查问题处理错误排查 DG同步完成后,DG Broker show database发现以下告警信息: Database Warning(s):ORA-16826: apply service state is inconsistent with the DelayMins propertyORA-16789: standby redo log…

开源与闭源之争:AI时代的创新博弈与未来抉择

在人工智能技术狂飙突进的今天&#xff0c;开源与闭源之争已不再局限于技术圈的讨论&#xff0c;而是演变为一场关乎技术伦理、商业格局乃至人类文明走向的深度博弈。当Meta的Llama 3开源模型下载量突破百万&#xff0c;当OpenAI的GPT-5继续加固技术壁垒&#xff0c;这场没有硝…

NIFI的处理器:JSLTTransformJSON 2.4.0

该处理器使用JSLT转换FlowFile JSON有效负载的格式。使用转换后的内容创建新的FlowFile&#xff0c;并将其路由到“成功”关系。如果JSLT转换失败&#xff0c;则将原始FlowFile路由到“失败”关系。 需要注意的是&#xff0c;编译JSLT转换可能相当昂贵。理想情况下&#xff0c…

MySQL 索引失效及其解决办法

一、前言 在数据库优化中,索引(Index)是一项至关重要的技术手段,可以显著提升查询性能。然而,在实际开发过程中,MySQL 索引并不总是如预期生效。本文将从原理出发,系统地介绍索引失效的常见场景及其解决方案,帮助开发者有效规避性能陷阱。 二、索引基础回顾 MySQL 支…

趋势触发策略

趋势触发策略(TS版)是一种基于TrendTriggerFactor(TTF)的交易策略,通过柱状图颜色变化指示市场趋势的强度,并根据TTF的穿越信号进行买卖操作。 TTF是通过计算买方力量和卖方力量的差值除以两者之和的一半再乘以100得到的。 当TTF大于100时,柱状图显示为绿色,表示市场…

DeepSeek-R1 模型现已在亚马逊云科技上推出

亚马逊云科技提供众多免费云产品&#xff0c;可以访问&#xff1a;亚马逊云科技 在刚刚过去的 Amazon re&#xff1a;Invent 期间&#xff0c;Amazon 首席执行官 Andy Jassy 分享了从 Amazon 自己在全公司开发近 1000 个生成式 AI 应用程序的经验中汲取的宝贵经验。从这种广泛…

中台项目-微前端qiankun-umimax

学习视频&#x1f50a; 基础&#xff1a; 黑马前端基于qiankun搭建微前端项目实战教程_哔哩哔哩_bilibili 路由、部署配置注意&#xff1a;qiankunvite微前端上线注意事项&#xff0c;base公共路径设置_哔哩哔哩_bilibili 微前端 什么是微前端&#xff1f; 微前端是将前端应…

【Java学习笔记】代码块

代码块 介绍&#xff1a;代码块又称为初始化块&#xff0c;属于类中的成员&#xff08;即是类的一部分&#xff09;&#xff0c;类似于方法&#xff0c;将逻辑语句封装在方法体中&#xff0c;通过{}包围起来 与类方法的不同点 无方法名 无返回类型 无参数 只有方法体&#…

spring boot 2.7集成旧的springfox-boot-starter swagger oas 3.0

旧版本目前已经不维护推荐使用 springdoc-openapi-ui&#xff0c;这里为了演示使用旧的最新依赖 <dependency><groupId>io.springfox</groupId><artifactId>springfox-boot-starter</artifactId><version>3.0.0</version> </dep…

Linux按键驱动测试方式详细介绍

Linux按键驱动测试可采用以下分层方法&#xff1a; 基础事件检测 使用输入子系统调试工具&#xff1a; sudo apt install evtest # 安装事件测试工具 evtest # 选择对应设备编号触发按键后观察终端输出&#xff0c;正常情况应显示&#xff1a; Event:…

USB学习【13】STM32+USB接收数据过程详解

目录 1.官方的描述2.HAL的流程把接收到的数据从PMA拷贝到用户自己定义的空间中 3.处理接收到的数据4.最后再次开启准备接收工作 1.官方的描述 2.HAL的流程 以上的官方说法我们暂时按下不表。 如果接收到数据&#xff0c;会激活中断进入到USB_LP_CAN1_RX0_IRQHandler&#xff0…