设计模式之适配器模式:软件世界的桥梁建筑师

news2024/11/15 4:44:05

一、什么是适配器模式

    适配器模式(Adapter Pattern)是一种结构型设计模式(Structural Pattern),通过将类的接口转换为客户期望的另一个接口,适配器可以让不兼容的两个类一起协同工作。其核心思想是通过一个中间的“适配器”类,将一个类的接口转换成客户端所期待的另一种接口形式,从而使得原本因接口不兼容而不能一起工作的类能够协同工作。就像是在现实生活中,国际旅行中常用的电源转换器,就是将不同国家的插座标准转换为你的电子设备所支持的充电接口,使设备得以顺利充电。

二、适配器模式的结构

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

  1. 目标接口(Target):这是客户端所期待的接口,即客户端通过此接口来调用所需的业务逻辑。

  2. 适配者(Adaptee):需要适配的类或接口,通常其现有的接口与客户端所需的接口不兼容。

  3. 适配器(Adapter):这是适配器模式的核心,它继承适配者或者持有一个适配者的引用,并通过实现目标接口,将适配者的接口转换为客户端所期望的接口。

三、适配器模式的分类

    根据适配器如何适配适配者,适配器模式可以分为两种类型:类适配器模式和对象适配器模式

1. 类适配器模式

    类适配器通过实现目标接口的同时继承适配者的方式,来实现在内部使用适配者的方法来实现目标接口的的方法:

2. 对象适配器模式

    对象适配器模式则是通过组合的方式来实现接口的适配。适配器类持有一个适配者的引用,并通过该引用来调用源角色的方法,从而实现目标接口的方法。

四、适配器模式的应用场景

    适配器模式广泛应用于各种软件开发场景中,尤其是当面临不同系统、不同库或不同版本之间的接口不兼容问题时,适配器模式往往能够提供优雅的解决方案。以下是一些典型的应用场景:

  1. 旧系统迁移:在将旧系统迁移到新系统时,新系统可能不支持旧系统的某些接口或方法。此时,可以通过适配器模式将旧系统的接口适配为新系统所需的接口,从而保持旧系统功能的可用性。

  2. 第三方库集成:在集成第三方库时,如果该库的接口与我们的系统接口不兼容,可以通过适配器模式来封装第三方库的接口,使其符合我们的系统架构和接口规范。

  3. 接口升级:在软件系统的演进过程中,有时需要对接口进行升级或重构,但升级后的接口可能与旧客户端不兼容。此时,可以通过适配器模式为旧客户端提供一个兼容层,使它们能够继续访问系统而不必立即进行更新。

  4. 多平台支持:在开发跨平台应用程序时,不同平台可能提供不同的API接口。通过适配器模式,可以将各个平台的API适配为统一的接口,从而简化开发工作和代码维护。

五、适配器模式示例

    为了更直观地理解适配器模式,以下通过一个简单的示例来说明其实现过程。

     假设我们有一个音频播放器系统,该系统支持多种音频格式的播放,但现在我们需要接入一个新的音频设备,该设备只支持MP3格式的音频。然而,我们的音频库中存在一些非MP3格式的音频文件(如WAV格式),我们需要将这些非MP3格式的音频文件转换为MP3格式后才能在该设备上播放。

1. 定义目标接口

    首先,假设存在一个目标接口AudioPlayer,表示音频播放器的功能接口。

public interface AudioPlayer {
    void play(String audioType, String fileName);
}

2. 定义适配者

    另外,存在一个适配者Mp3Player,它代表了一个只支持MP3格式的播放器。

public class Mp3Player {
    public void playMp3(String fileName) {
        System.out.println("Playing mp3 file. Name: " + fileName);
    }
}

    注意:这里Mp3Player并没有直接提供play方法,而是提供了针对特定格式的播放方法,如playMp3。

3. 创建适配器类

    现在,我们需要创建一个适配器类AudioAdapter,它实现了AudioPlayer接口,并持有一个Mp3Player的实例,以便将非MP3格式的音频文件转换为MP3格式(在这个例子中,为了简化,我们假设转换逻辑已经内置在AudioAdapter中,实际上可能需要外部转换工具或服务)。

    然而,为了保持示例的简洁性和集中讨论适配器模式的核心思想,我们将省略实际的转换逻辑,而是直接调用相应的播放方法,并输出一个假设的转换过程。

public class AudioAdapter implements AudioPlayer {
    private final Mp3Player mp3Player;
​
    public AudioAdapter(Mp3Player mp3Player) {
        this.mp3Player = mp3Player;
    }
​
    @Override
    public void play(String audioType, String fileName) {
        if ("wma".equalsIgnoreCase(audioType)) {
            // 假设这里进行了wma到MP3的转换
            System.out.println("Converting wma to mp3 format...");
        } else if ("wav".equalsIgnoreCase(audioType)) {
            // 假设这里进行了wav到MP3的转换
            System.out.println("Converting wav to mp3 format...");
        }
        mp3Player.playMp3(fileName);
    }
}

    请注意,上述代码中的play方法通过检查audioType参数来确定要播放的音频格式,并假设进行了一些格式转换。这里的关键点是适配器类AudioAdapter如何将一个不直接支持的接口(在这个例子中是play方法,其期望接收音频类型和文件名)适配为另一个接口(Mp3Player中的playMp3方法)。

4. 使用适配器

    最后,我们可以创建一个测试类来模拟使用这个适配器。

public class AudioPlayerTest {
    public static void main(String[] args) {
        AudioPlayer audioPlayer = new AudioAdapter(new Mp3Player());
        audioPlayer.play("mp3", "music.mp3");
        audioPlayer.play("wma", "music.wma");
        audioPlayer.play("wav", "music.wav");
    }
}

5. 运行结果

    在上面的例子中,我们创建了一个AudioPlayer类型的对象,但实际上它是一个AudioAdapter的实例,该实例内部封装了一个Mp3Player对象。通过AudioAdapter,我们能够以AudioPlayer接口期望的方式(即调用play方法并传入音频类型和文件名)来播放非MP3格式的音频文件,尽管AdvancedAudioPlayer本身并不支持这种调用方式。

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

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

相关文章

嵌入式全栈开发学习笔记---Linux系统编程(概述)

目录 入门级问题 为什么要学习Linux系统? 为什么Linux系统被嵌入式设备广泛应用? 系统调用 应用层是什么? 系统调用和库函数有什么区别? 为什么在应用层不能直接调用内核中的函数? 为什么有了系统调用就安全了…

Linux系统安装MySQL8.0

1.查看Linux发行版 2.安装前准备 2.1.检查是否安装 rpm -qa | grep mysql 2.2.如已安装mysql,则删除 rpm -e --nodeps 包名 2.3.再次检查安装包是否全部删除 rpm -qa | grep mysql 2.4.搜索mysql文件夹 find / -name mysql 2>/dev/null 2.5.若有mysql文件夹&a…

Golang | Leetcode Golang题解之第388题文件的最长绝对路径

题目&#xff1a; 题解&#xff1a; func lengthLongestPath(input string) (ans int) {n : len(input)level : make([]int, n1)for i : 0; i < n; {// 检测当前文件的深度depth : 1for ; i < n && input[i] \t; i {depth}// 统计当前文件名的长度length, isFi…

Cookie对象的缺陷与应对策略

Cookie对象的缺陷与应对策略 1. 安全性问题&#xff1a;Cookie是明文的2. 存储限制&#xff1a;浏览器对Cookie数量和大小有限制3. 性能影响&#xff1a;Cookie携带过多增加网络流量4. 数据类型限制&#xff1a;Cookie的value值只能是字符串 &#x1f496;The Begin&#x1f4…

82.给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点,只留下不同的数字 。实现返回已排序的链表

删除排序链表中的重复元素 II 一、题目描述 82. 删除排序链表中的重复元素 II 给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。 示例 1: 输入:head = [1,2,3,3,4,4,5] 输出:[1,2,5] 示例 2: 输入:hea…

华为云征文|部署电影收藏管理器 Radarr

华为云征文&#xff5c;部署电影收藏管理器 Radarr 一、Flexus云服务器X实例介绍1.1 云服务器介绍1.2 应用场景1.3 核心竞争力 二、Flexus云服务器X实例配置2.1 重置密码2.2 服务器连接2.3 安全组配置 三、部署 Radarr3.1 Radarr 介绍3.2 Docker 环境搭建3.3 Radarr 部署3.4 Ra…

ubuntu24安装cuda和cudnn

一、安装cuda 确保显卡驱动正确安装 终端输入&#xff1a; nvidia-smi显示下面结果&#xff0c;说明显卡驱动安装正常&#xff0c;可以进行下一步 1.去官网下载CUDA&#xff0c;需要注册账号下载 https://developer.nvidia.com/cuda-toolkit-archive由于我们显卡支持12.2&…

[免越狱]FLEXTool/FLEX 炫酷功能怎么添加到目标App

原因&#xff1a; 这么炫酷的功能&#xff0c;手机又不想越狱&#xff0c;又想体验一波&#xff0c;于是研究了他源码&#xff0c;实现了功能。 1. 下载源码 https://github.com/FLEXTool/FLEX?tabreadme-ov-filehttps://github.com/FLEXTool/FLEX?tabreadme-ov-file 2. 修…

TeamTalk消息服务器(未读计数)

信令和协议设计 enum MessageCmdID {// ...... 省略无关逻辑 CID_MSG_UNREAD_CNT_REQUEST 775,CID_MSG_UNREAD_CNT_RESPONSE 776,// ...... 省略无关逻辑 };message IMUnreadMsgCntReq{//cmd id: 0x0307required uint32 user_id 1;optional bytes attach_data 20; }mes…

[Labview]图片叠加下的表格视图拖拽功能:挖坑粗糙版

没错&#xff0c;又是Labview表格T - T 由于项目中用到的表格上有一张用于画框的二维图片&#xff0c;感兴趣可看这篇 [Labview] 表格单元格外边框 二维图片叠加绘图 因此在滚动条与鼠标滚轮的基础上&#xff0c;想再增加一个拖拽移动的功能。 但 [二维图片] 并没有 拖拽开始…

windows C++ 并行编程-矩阵乘法

下面我们尝试分步演练演示如何使用 C AMP 加速矩阵乘法的执行。 提供了两种算法&#xff0c;一种不使用平铺&#xff0c;一种使用平铺&#xff0c;看看两者的差别。 在 Visual Studio 中创建项目 在菜单栏上&#xff0c;选择“文件”>“新建”>“项目”&#xff0c;打开…

[Algorithm][综合训练][过桥][最大差值][兑换零钱]详细讲解

目录 1.过桥1.题目链接2.算法原理详解 && 代码实现 2.最大差值1.题目链接2.算法原理详解 && 代码实现 3.兑换零钱1.题目链接2.算法原理详解 && 代码实现 1.过桥 1.题目链接 过桥 2.算法原理详解 && 代码实现 解法&#xff1a;贪心 BFS #in…

【Slurm集群在centos7上的搭建】

Slurm集群在centos7上的部署 集群基本情况1. 前期准备工作2.网络配置3.NTP时间同步配置4.NFS共享目录配置5.NIS用户管理配置6.Munge通信部署7.安装Mariadb数据库以及Slurm安装配置7.1安装配置Mariadb及SlurmID配置7.2Slurm安装配置 附录配置文件slurm.conf&#xff1a;slurmdbd…

Redis从入门再再到入门(下)

文章目录 1.Redis远程连接1.1 Redis远程连接配置1.2 通过桌面版图形化界面连接Redis1.3 通过IDEA中的插件连接Redis 2.Jedis的基本使用2.1 jedis概述2.2 jedis的基本操作2.3 jedis连接池 3.Spring整合Redis3.1 新建maven工程,引入相关依赖3.2 redis.properties3.3 spring-redis…

Python | Leetcode Python题解之第387题字符串中的第一个唯一字符

题目&#xff1a; 题解&#xff1a; class Solution:def firstUniqChar(self, s: str) -> int:position dict()q collections.deque()n len(s)for i, ch in enumerate(s):if ch not in position:position[ch] iq.append((s[i], i))else:position[ch] -1while q and po…

如何开发针对不平衡分类的成本敏感神经网络 python

如何开发针对不平衡分类的成本敏感神经网络 深度学习神经网络是一类灵活的机器学习算法&#xff0c;可以在各种问题上表现良好。 神经网络使用误差反向传播算法进行训练&#xff0c;该算法涉及计算模型在训练数据集上产生的误差&#xff0c;并根据这些误差的比例更新模型权重…

鸿蒙开发入门day16-拖拽事件和手势事件

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;还请三连支持一波哇ヾ(&#xff20;^∇^&#xff20;)ノ&#xff09; 目录 拖拽事件 概述 拖拽流程 ​手势拖拽 ​鼠标拖拽 拖拽背板图 …

如何有效防止表单重复提交

如何有效防止表单重复提交 1. 使用重定向&#xff08;Redirect&#xff09;2. 点击后按钮失效3. Loading 遮罩4. 自定义重复提交过滤器 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在Web开发中&#xff0c;表单重复提交是一个常见问题&…

计算物理精解【3】

文章目录 力学单位矢量基础定义 矢量加法矢量加法的几何方法矢量加法的代数方法示例注意事项 矢量间的关系矢量&#xff08;或向量&#xff09;的标量积&#xff08;也称为点积、内积或数量积&#xff09;性质计算两矢量之间的夹角例子步骤数值结果 计算两三维矢量之间夹角的例…

厨房老鼠检测算法解决方案老鼠检测算法源码样本详细介绍

厨房老鼠检测算法是一种创新的解决方案&#xff0c;它结合了机器学习和图像识别技术。通过使用高精度的传感器和智能摄像头&#xff0c;这些算法可以实时监控厨房环境&#xff0c;并检测到老鼠的活动痕迹。与传统的检测方法相比&#xff0c;这种算法具有更高的灵敏度和准确性&a…