Spring框架之适配器模式 (Adapter Pattern)

适配器模式(Adapter Pattern)详解

适配器模式(Adapter Pattern)是一种结构型设计模式,它的主要作用是将一个类的接口转换成客户端期望的另一个接口,使原本由于接口不兼容而无法一起工作的类可以协同工作。适配器模式通常被用来修复已有代码的兼容性问题,尤其是在重用现有类时避免大规模修改代码。

1. 适配器模式的定义

1.1 什么是适配器模式?

适配器模式将 不兼容的接口 进行适配,使得原本不兼容的类能够在一起工作。它就像生活中的电源适配器,可以让不同国家的电器设备在本国的电网环境下正常工作。

1.2 适配器模式的特点
  • 转换接口:将一个类的接口转换为客户端所期望的另一个接口。
  • 兼容性:解决接口不兼容的问题,让原本无法协作的类能够协同工作。
  • 封装旧接口:适配器模式常用于将旧系统的接口封装起来,以便与新系统兼容。

2. 适配器模式的结构

适配器模式通常包含以下几个角色:

  1. Target(目标接口)
    • 客户端期望使用的接口。
  2. Adaptee(被适配者)
    • 需要适配的类,其接口与目标接口不兼容。
  3. Adapter(适配器)
    • 将被适配者的接口转换为目标接口的一个包装类。
  4. Client(客户端)
    • 使用目标接口的类。
类图
    +-------------+|   Target    |<--------------------++-------------+                     || + request() |                     |+-------------+                     |^                           ||                           |+--------------+          +----------------+|   Adapter    |--------->|   Adaptee      |+--------------+          +----------------+| + request()  |          | + specificRequest() |+--------------+          +----------------+

3. 适配器模式的分类

适配器模式有两种主要实现方式:

  1. 类适配器(Class Adapter)
    • 使用继承来实现适配器。适配器类继承自 AdapteeTarget
    • 由于 Java 只支持单继承,因此在 Java 中类适配器使用场景较少。
  2. 对象适配器(Object Adapter)
    • 使用组合来实现适配器。适配器类持有一个 Adaptee 对象的引用。
    • 这种方式更加灵活,是推荐的实现方式。

4. 适配器模式的实现

为了更好地理解适配器模式,我们使用一个简单的示例来演示其工作原理。假设我们有一个 AudioPlayer 类,它只能播放 .mp3 文件,现在我们希望它也能播放 .mp4.vlc 文件。

4.1 Java 示例代码:对象适配器
// 目标接口
interface MediaPlayer {void play(String audioType, String fileName);
}// 被适配者类(支持 VLC 播放)
class VlcPlayer {public void playVlc(String fileName) {System.out.println("播放 VLC 文件: " + fileName);}
}// 被适配者类(支持 MP4 播放)
class Mp4Player {public void playMp4(String fileName) {System.out.println("播放 MP4 文件: " + fileName);}
}// 适配器类
class MediaAdapter implements MediaPlayer {private VlcPlayer vlcPlayer;private Mp4Player mp4Player;public MediaAdapter(String audioType) {if (audioType.equalsIgnoreCase("vlc")) {vlcPlayer = new VlcPlayer();} else if (audioType.equalsIgnoreCase("mp4")) {mp4Player = new Mp4Player();}}@Overridepublic void play(String audioType, String fileName) {if (audioType.equalsIgnoreCase("vlc")) {vlcPlayer.playVlc(fileName);} else if (audioType.equalsIgnoreCase("mp4")) {mp4Player.playMp4(fileName);}}
}// 具体目标类
class AudioPlayer implements MediaPlayer {private MediaAdapter mediaAdapter;@Overridepublic void play(String audioType, String fileName) {if (audioType.equalsIgnoreCase("mp3")) {System.out.println("播放 MP3 文件: " + fileName);} else if (audioType.equalsIgnoreCase("vlc") || audioType.equalsIgnoreCase("mp4")) {mediaAdapter = new MediaAdapter(audioType);mediaAdapter.play(audioType, fileName);} else {System.out.println("不支持的文件格式: " + audioType);}}
}// 测试客户端
public class AdapterPatternDemo {public static void main(String[] args) {AudioPlayer audioPlayer = new AudioPlayer();audioPlayer.play("mp3", "歌曲.mp3");audioPlayer.play("mp4", "电影.mp4");audioPlayer.play("vlc", "动画.vlc");audioPlayer.play("avi", "短片.avi");}
}

输出结果

播放 MP3 文件: 歌曲.mp3
播放 MP4 文件: 电影.mp4
播放 VLC 文件: 动画.vlc
不支持的文件格式: avi

5. 适配器模式的应用场景

适配器模式适合以下场景:

  1. 希望复用一些现有的类,但是类的接口不符合当前系统的需求。
  2. 想要封装一些旧系统的类,以便在新系统中使用。
  3. 使用第三方库,而其接口与项目中定义的接口不兼容。
  4. 希望创建一个可复用的类,使其能够与不相关或不可预见的类协同工作。

6. 适配器模式的优缺点

6.1 优点
  • 提高类的复用性:通过适配器模式,现有类可以与其他类进行协作,而无需修改原始类的代码。
  • 提高类的灵活性和扩展性:通过引入适配器类,可以在不修改现有代码的情况下增加新功能。
  • 解耦目标类与被适配者类:适配器将目标类和被适配者类隔离开来,实现了解耦。
6.2 缺点
  • 增加系统复杂度:引入适配器后,系统会增加额外的适配器类,导致复杂度增加。
  • 不易理解:尤其是系统中存在大量适配器时,可能会导致代码难以理解和维护。
  • 类适配器的实现受限于继承:在 Java 中,类适配器受限于单继承的特性,因此更推荐使用对象适配器。

7. 适配器模式的扩展

7.1 双向适配器
  • 适配器模式可以实现双向适配,即适配器不仅能够将被适配者转化为目标接口,也能将目标接口转化为被适配者接口。
7.2 缺省适配器(Default Adapter)
  • 适用于接口中有多个方法时,想要简化接口的实现。通常通过创建一个抽象类,该抽象类提供这些方法的默认实现,具体类只需重写需要的方法。

8. 适配器模式的实际应用

  1. Java I/O 类库
    • Java 的 InputStreamReaderOutputStreamWriter 就是典型的适配器模式,用于在字节流和字符流之间进行适配。
  2. 数据库连接
    • JDBC 中的 DriverManager 使用适配器模式来适配不同的数据库驱动程序。
  3. Spring 框架
    • Spring 的 HandlerAdapter 适配器用于将不同类型的请求处理器(如 ControllerHttpRequestHandler)适配成统一的处理方式。

9. 总结

适配器模式是一种非常有用的设计模式,尤其是在需要与现有系统集成时。通过适配器模式,可以有效地复用现有代码,同时保持系统的灵活性和扩展性。

  • 优点:提高复用性、解耦、增加灵活性。
  • 缺点:增加复杂度、不易理解。
  • 适用场景:复用现有类、封装旧系统、使用第三方库。

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

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

相关文章

10款PDF合并工具的使用体验与推荐!!!

在如今的信息洪流中&#xff0c;我们几乎每个人都被淹没在大量的数字文档之中。无论是学生、教师还是职场人士&#xff0c;我们都需要高效地管理和处理这些文档。而PDF文件&#xff0c;凭借其跨平台的稳定性和通用性&#xff0c;成了最常用的文档格式之一。我们经常需要处理、编…

Redis做分布式锁

&#xff08;一&#xff09;为什么要有分布式锁以及本质 在一个分布式的系统中&#xff0c;会涉及到多个客户端访问同一个公共资源的问题&#xff0c;这时候我们就需要通过锁来做互斥控制&#xff0c;来避免类似于线程安全的问题 因为我们学过的sychronized只能对线程加锁&…

IntelliJ+SpringBoot项目实战(四)--快速上手数据库开发

对于新手学习SpringBoot开发&#xff0c;可能最急迫的事情就是尽快掌握数据库的开发。目前数据库开发主要流行使用Mybatis和Mybatis Plus,不过这2个框架对于新手而言需要一定的时间掌握&#xff0c;如果快速上手数据库开发&#xff0c;可以先按照本文介绍的方式使用JdbcTemplat…

12、Linux系统的网络基本设置

查看网络接口信息ifconfig ip addr/ip a #简单查看网络接口信息 ifconfig #表示只显示当前活跃的设备接口信息 ifconfig -a #查看当前主机所有的&#xff08;all&#xff09;网络设备&#xff0c;包括未运行的设备。 如我们查看本机网卡ens33的…

JDK1.8升级JDK不生效

最近因为项目原因&#xff0c;需要将jdk1.8升级到JDK11.升级发生了一个纠结的问题&#xff0c;就是cmd不生效。在此记录&#xff01; 项目中指定jdk 如果在android studio项目&#xff0c;可以单独指定该项目的jdk&#xff0c;而不用全局升级&#xff0c;可以做如下配置&#…

八 Bean的生命周期

八、Bean的生命周期 8.1 什么是Bean的生命周期 Spring其实就是一个管理Bean对象的工厂。它负责对象的创建&#xff0c;对象的销毁等。 所谓的生命周期就是&#xff1a;对象从创建开始到最终销毁的整个过程。 什么时候创建Bean对象&#xff1f; 创建Bean对象的前后会调用什…

【Android】webview常用方法和使用

文章目录 前言一、常见用法二、基础属性webView的常用方法WebViewClient的常用方法WebChromeClient的常用方法WebSettings的相关方法 三、加载流程和事件回调四、webview和JS之间的互相调用总结 五、参考链接 前言 最近项目又用到了webview&#xff0c;在回顾复习一次webview相…

OpenGL ES 共享上下文实现多线程渲染

OpenGL ES 共享上下文时,可以共享哪些资源? 共享上下文实现多线程渲染 EGL 概念回顾 EGL 是 OpenGL ES 和本地窗口系统(Native Window System)之间的通信接口,它的主要作用: 与设备的原生窗口系统通信; 查询绘图表面的可用类型和配置; 创建绘图表面; 在OpenGL ES 和…

09C++结构体

/*结构体属于用户自定义的数据类型&#xff0c; 允许用户存储不同的数据类型, 语法:struct 结构体名{结构体成员列表} ;*/ //struct 结构体名 变量名 #include <iostream> #include <string> using namespace std; struct student { string name; int age;int s…

python第七次作业

01.设计一个函数&#xff0c;可以传入一个或多个单词的字符串&#xff0c;并返回该字符串&#xff0c;但所有五个或更多字母的单词都前后颠倒 a input("输入:") print(a) #将一句话以空格为分界拆分为单个单词 b a.split(" ") ls_1 [] ls_2 []for i i…

C++开发基础之使用librabbitmq库实现RabbitMQ消息队列通信

1. 前言 RabbitMQ是一个流行的开源消息队列系统&#xff0c;支持多种消息协议&#xff0c;广泛用于构建分布式系统和微服务架构。可以在不同应用程序之间实现异步消息传递。在本文中&#xff0c;我们将熟悉如何使用C与RabbitMQ进行消息通信。 2. 准备工作 在 Windows 平台上…

AI写作(四)预训练语言模型:开启 AI 写作新时代(4/10)

一、预训练语言模型概述 ​ 预训练语言模型在自然语言处理领域占据着至关重要的地位。它以其卓越的语言理解和生成能力&#xff0c;成为众多自然语言处理任务的关键工具。 预训练语言模型的发展历程丰富而曲折。从早期的神经网络语言模型开始&#xff0c;逐渐发展到如今的大规…

图像处理实验一(Matlab Exercises and Image Fundamentals)

一、基本概念介绍 MATLAB是一种广泛使用的高性能语言&#xff0c;特别适用于数学计算、算法开发、数据分析和可视化。在图像处理领域&#xff0c;MATLAB提供了强大的工具和函数&#xff0c;使得图像的读取、处理和分析变得相对简单。通过MATLAB&#xff0c;用户可以实现从基本的…

番外-JDBC:2024年最新java连接数据库教程

前言 JavaScript的内容晚点更新&#xff0c;今天继续更新一点番外&#xff0c;今天更新的是jdbc&#xff0c;如何用java连接数据库 1.导包 要使java能够连接数据库我们需要导入一个包&#xff0c;请按照以下操作安装并导包 1.进入官网 MySQL 以上为官网链接进去后点击下载…

Ubuntu22.04安装DataEase

看到DataEase的驾驶舱&#xff0c;感觉比PowerBI要好用一点&#xff0c;于是搭建起来玩玩。Dataease推荐的操作系统是Ubuntu22.04/Centos 7。 下载了Ubuntu22.04和DataEase 最新版本的离线安装包 一.安装ubuntu22.04 在安装的时候&#xff0c;没有顺手设置IP地址信息&#xff…

vueRouter路由切换时实现页面子元素动画效果, 左右两侧滑入滑出效果

说明 vue路由切换时&#xff0c;当前页面左侧和右侧容器分别从两侧滑出&#xff0c;新页面左右分别从两侧滑入 效果展示 路由切换-滑入滑出效果 难点和踩坑 现路由和新路由始终存在一个页面根容器&#xff0c;通过<transition>组件&#xff0c;效果只能对页面根容器有效…

acwing算法基础03-递归,枚举

cWing 93. 递归实现组合型枚举 1.排序 考虑顺序 2. 组合 不考虑顺序 参数 -核心 递归 模板 1.指数型 选/不选 2. 排列 -考虑顺序 &#xff08;判重数组 不知道哪个数有有没有用过&#xff09;3.组合 不考虑顺序 数据范围 从n个数里选m个数 组合数中间点 取范围 #includ…

ASP.NET 部署到IIS,访问其它服务器的共享文件 密码设定

asp.net 修改上面的 IIS需要在 配置文件 添加如下内容 》》》web.config <system.web><!--<identity impersonate"true"/>--><identity impersonate"true" userName"您的账号" password"您的密码" /><co…

多角度审视推荐系统

参考自《深度学习推荐系统》——王喆&#xff0c;用于学习和记录 介绍 推荐工程师需要从不同的维度审视推荐系统&#xff0c;不仅抓住问题的核心&#xff0c;更要从整体上思考推荐问题。 具体包括以下内容&#xff1a; &#xff08;1&#xff09;推荐系统如何选取和处理特征…

从0开始机器学习--Day23--支持向量机

经过前面的学习&#xff0c;我们已经知道在解决问题时&#xff0c;重要的不仅仅是要在算法A或算法B中选择更优的&#xff0c;而是考虑怎么选择用于学习算法的特征和正则化参数&#xff0c;相比神经网络和逻辑回归&#xff0c;支持向量机在这两个方面做得更好。 优化目标(Optimi…