设计模式 结构型 适配器模式(Adapter Pattern)与 常见技术框架应用 解析

在这里插入图片描述

适配器模式(Adapter Pattern)是一种结构型设计模式,它允许将一个类的接口转换成客户端所期望的另一个接口,从而使原本因接口不兼容而无法一起工作的类能够协同工作。这种设计模式在软件开发中非常有用,尤其是在需要集成不同系统或库时,它们的接口可能并不一致。

一、核心思想

适配器模式的核心思想是通过创建一个中间层(适配器),使得原本由于接口不兼容而无法一起工作的类可以协同工作。这个中间层负责将源接口转换为目标接口,从而在客户端和目标类之间提供一个桥梁。

二、定义与结构

定义:将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

结构

  • 目标接口(Target):定义了客户端期望的接口。
  • 被适配者(Adaptee):需要被适配的类,其接口与目标接口不兼容。
  • 适配器(Adapter):实现了目标接口,并持有被适配者的实例。适配器通过调用被适配者的方法来实现目标接口的方法。
角色

在适配器模式中,通常包含以下角色:

  • 目标角色(Target):定义了客户端需要使用的接口。
  • 源角色(Adaptee):需要被适配的接口,它与目标接口不兼容。
  • 适配器角色(Adapter):负责将源接口转换成目标接口,使得客户端可以通过目标接口与适配器交互,而无需知道具体的被适配者。
  • 客户类(Client):在客户类中针对目标抽象类进行编程,调用在目标抽象类中定义的业务方法。

三、实现步骤及代码示例

1、定义目标接口

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

这里定义了一个简单的媒体播放器接口 MediaPlayer,它期望能播放指定音频类型和文件名的音频文件。

2、定义适配者类

public class AdvancedMediaPlayer {
    public void playVlc(String fileName) {
        System.out.println("Playing vlc file: " + fileName);
    }
    public void playMp4(String fileName) {
        System.out.println("Playing mp4 file: " + fileName);
    }
}

AdvancedMediaPlayer 是已有的高级媒体播放器类,能播放 VLCMP4 格式文件,但接口与 MediaPlayer 不同,是需要适配的对象。

3、实现适配器类

public class MediaAdapter implements MediaPlayer {
    private AdvancedMediaPlayer advancedMediaPlayer;

    public MediaAdapter(String audioType) {
        if ("vlc".equals(audioType)) {
            advancedMediaPlayer = new AdvancedMediaPlayer();
            advancedMediaPlayer.playVlc(audioType);
        } else if ("mp4".equals(audioType)) {
            advancedMediaPlayer = new AdvancedMediaPlayer();
            advancedMediaPlayer.playMp4(audioType);
        }
    }

    @Override
    public void play(String audioType, String fileName) {
        if ("vlc".equals(audioType)) {
            advancedMediaPlayer.playVlc(fileName);
        } else if ("mp4".equals(audioType)) {
            advancedMediaPlayer.playMp4(fileName);
        }
    }
}

MediaAdapter 实现了 MediaPlayer 目标接口,内部根据传入音频类型实例化 AdvancedMediaPlayer,并在 play 方法中调用适配者对应方法来播放文件,完成接口适配。

4、客户端使用示例

public class AudioPlayer implements MediaPlayer {
    private MediaAdapter mediaAdapter;

    @Override
    public void play(String audioType, String fileName) {
        if ("mp3".equals(audioType)) {
            System.out.println("Playing mp3 file: " + fileName);
        } else if (("vlc".equals(audioType)) || ("mp4".equals(audioType))) {
            mediaAdapter = new MediaAdapter(audioType);
            mediaAdapter.play(audioType, fileName);
        } else {
            System.out.println("Invalid audio type");
        }
    }
}

public class Main {
    public static void main(String[] args) {
        AudioPlayer audioPlayer = new AudioPlayer();
        audioPlayer.play("mp3", "song.mp3");
        audioPlayer.play("vlc", "video.vlc");
        audioPlayer.play("mp4", "movie.mp4");
    }
}

AudioPlayer 作为客户端类,它本身能播放 MP3 文件,对于 VLCMP4 文件则借助 MediaAdapter 适配,在 main 程序入口,演示了多种音频格式播放,体现适配器模式使不同接口协同工作。

四、常见技术框架应用

1、在 Python 的 Django 框架中的应用

在 Django 项目中,若要整合第三方认证系统,其返回的用户数据格式与 Django 内置的用户模型格式不一致。假设第三方认证返回用户信息是一个字典 {"name": "John", "email": "john@example.com", "age": 30},而 Django 用户模型期望通过实例化 User 类,传入 usernameemail 等参数来创建用户。

from django.contrib.auth.models import User

# 适配者类,模拟第三方认证返回数据格式
class ThirdPartyUserData:
    def __init__(self, user_data):
        self.user_data = user_data

    def get_name(self):
        return self.user_data["name"]

    def get_email(self):
        return self.user_data["email"]

    def get_age(self):
        return self.user_data["age"]

# 适配器类
class DjangoUserAdapter:
    def __init__(self, third_party_user_data):
        self.third_party_user_data = third_party_user_data

    def create_django_user(self):
        name = self.third_party_user_data.get_name()
        email = self.third_party_user_data.get_email()
        username = name.lower().replace(" ", "")
        user = User.objects.create(username=username, email=email)
        return user

# 客户端使用
third_party_user_data = ThirdPartyUserData({"name": "John Doe", "email": "johndoe@example.com", "age": 30})
adapter = DjangoUserAdapter(third_party_user_data)
new_user = adapter.create_django_user()
print(new_user)

这里 ThirdPartyUserData 是适配者,提供第三方原始用户数据格式。DjangoUserAdapter 是适配器,将第三方数据转换为符合 Django 用户模型创建的格式,使第三方认证能无缝接入 Django 项目。

2、在 JavaScript 的 Vue.js 框架中的应用

假设在 Vue 项目中有一个旧的图表组件 OldChart,它接收的数据格式是一个包含 labelsvalues 的二维数组 [["label1", "label2"], ["value1", "value2"]],但新的业务需求要求使用 echarts 库绘制图表,echarts 所需数据格式是一个对象 { xAxis: ["label1", "label2"], yAxis: ["value1", "value2"] }

<template>
  <div id="app">
    <old-chart ref="oldChart" :data="oldData" />
    <echarts ref="echarts" :data="echartsData" />
  </div>
</template>

<script>
import OldChart from './OldChart.vue';
import echarts from 'echarts';

// 适配者:旧图表组件
export default {
  components: { OldChart },
  data() {
    return {
      oldData: [["January", "February"], ["10", "20"]],
      echartsData: null
    };
  },
  mounted() {
    // 适配器
    const adapter = {
      convertData(oldData) {
        const [labels, values] = oldData;
        return { xAxis: labels, yAxis: values };
      }
    };
    const echartsData = adapter.convertData(this.oldData);
    this.echartsData = echartsData;
    const myChart = echarts.init(this.$refs.echarts.$el);
    myChart.setOption({
      xAxis: {
        type: 'category',
        data: echartsData.xAxis
      },
      yAxis: {
        type: 'value'
      },
      series: [
        {
          data: echartsData.yAxis,
          type: 'bar'
        }
      ]
    });
  }
};
</script>

这里 OldChart 是适配者,adapterconvertData 方法作为适配器功能,将旧数据格式转换为 echarts 需要的格式,使两种图表组件能在同一项目中共存并按需使用。

五、应用场景

  1. 旧系统的兼容性问题:当需要使用一个已有系统,但它的接口与新系统不兼容时,可以通过适配器模式进行适配。
  2. 第三方库整合:当使用第三方库的接口与当前项目需求不一致时,可以通过适配器封装以符合需求。
  3. 统一接口:在多种类似功能的接口中,适配器可以对不同实现进行封装,提供统一的访问接口。
  4. 数据格式转换:在不同数据格式之间进行转换,如将JSON数据转换为XML数据。
  5. 硬件设备驱动:将不同厂商的硬件设备接口统一适配为系统标准接口。
  6. 图像绘制系统:现有一个老版本的绘图类LegacyRenderer,需要将其适配到新的绘图接口NewRenderer,以兼容新功能。
  7. 支付系统整合:整合多个第三方支付接口(如PayPal、Stripe)到统一的支付系统中。
  8. 日志框架适配器:将不同日志框架的接口统一适配为系统标准接口。
  9. 数据库适配器:将不同数据库供应商的API转换为统一的数据库访问接口,以便在不同的数据库之间切换和使用。
  10. 网络通信协议转换:在不同网络通信协议之间进行转换,如将HTTP请求转换为WebSocket请求。
  11. 操作系统平台差异处理:在不同操作系统平台(如Windows和Linux)之间进行文件路径或命令行参数的差异处理。
  12. 多媒体文件格式转换:在不同多媒体文件格式之间进行转换,如将MP3文件转换为WAV文件。
  13. 用户界面适配:在不同用户界面风格或布局之间进行适配,以满足不同用户的需求。
  14. 游戏控制器适配:将不同品牌或型号的游戏控制器适配为统一的游戏控制接口。
  15. 虚拟设备模拟:在软件开发过程中,模拟不存在的硬件设备或软件组件,以便进行测试或开发。
  16. 云服务集成:将不同云服务提供商的API转换为统一的接口,以便在多个云服务提供商之间无缝切换。
  17. 跨语言编程:在不同编程语言之间进行互操作,如将Java对象转换为Python对象。
  18. 消息队列集成:将不同消息队列系统的接口统一适配为系统标准接口。
  19. 缓存策略适配:将不同缓存策略的实现统一适配为系统标准接口。
  20. 安全认证机制适配:将不同安全认证机制的实现统一适配为系统标准接口。
  21. 国际化支持:将不同国际化方案的实现统一适配为系统标准接口。
  22. 时间日期格式转换:在不同时间日期格式之间进行转换,如将UNIX时间戳转换为人类可读的日期格式。
  23. 货币汇率转换:在不同货币之间进行汇率转换。
  24. 单位换算:在不同单位之间进行换算,如将英里转换为公里。
  25. 传感器数据适配:将不同传感器的数据格式统一适配为系统标准接口。
  26. 配置文件解析:将不同配置文件格式的解析结果统一适配为系统标准接口。

六、优缺点

优点

  1. 提高复用性:适配者类往往是已有且经过实践检验的代码,适配器模式让其能在新的目标接口场景下复用,避免重复开发类似功能。
  2. 增强系统扩展性:当需要接入新的不兼容模块或接口时,只需新增适配器类,无需改动原有系统核心代码,符合开闭原则,便于系统持续演进。
  3. 解耦系统组件:将接口转换逻辑封装在适配器中,使目标接口与适配者独立发展,降低两者直接耦合度,系统各部分维护、升级更便利。

缺点

  1. 额外的复杂性:引入适配器增加了系统类的数量与层次,如果过度使用,会使代码结构略显复杂,尤其在调试时,需追踪适配器内部逻辑及适配者原始接口,增加理解成本。
  2. 性能损耗:适配器在运行时需进行接口转换、数据格式调整等操作,相较于直接调用原生兼容接口,可能会有一定的性能损失,不过在多数非性能敏感场景下可接受。

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MCU芯片是什么意思_有哪些作用?

MCU(Microcontroller Unit)芯片&#xff0c;即微控制单元&#xff0c;是一种集成了中央处理器(CPU)、存储器(ROM、RAM)以及各种外设接口(如输入输出引脚、定时器、串口等)的集成电路芯片。它通过超大规模集成电路技术&#xff0c;将具有数据处理能力的中央处理器、随机存储器、…

如何免费解锁 IPhone 网络

您是否担心 iPhone 上的网络锁定&#xff1f;如果您的 iPhone 被锁定到特定运营商&#xff0c;解锁它可以连接到不同的运营商。好吧&#xff0c;我们为您准备了一份指南。 iPhone运营商免费解锁将是小菜一碟。在我们的解锁运营商 iphone 免费指南中。我们为您提供了一份简介&am…

Spring Security(maven项目) 3.0.2.4版本

前言&#xff1a; 通过实践而发现真理&#xff0c;又通过实践而证实真理和发展真理。从感性认识而能动地发展到理性认识&#xff0c;又从理性认识而能动地指导革命实践&#xff0c;改造主观世界和客观世界。实践、认识、再实践、再认识&#xff0c;这种形式&#xff0c;循环往…

计算机的错误计算(二百)

摘要 用三个大模型计算 exp(123.456). 结果保留10位有效数字。三个大模型的输出均是错误的&#xff0c;虽然其中一个给出了正确的 Python代码。 例1. 计算 exp(123.456). 保留10位有效数字。 下面是与第一个大模型的对话。 以上为与一个大模型的对话。 下面是与另外一个大模…

Golang的缓存一致性策略

Golang的缓存一致性策略 一致性哈希算法 在Golang中&#xff0c;缓存一致性策略通常使用一致性哈希算法来实现。一致性哈希算法能够有效地解决缓存节点的动态扩容、缩容时数据重新分布的问题&#xff0c;同时能够保证数据访问的均衡性。 一致性哈希算法的核心思想是将节点的哈希…

.e01, ..., .e0n的分卷压缩包怎么解压

用BandiZip&#xff0c;这些分卷压缩中还有一个.exe的文件&#xff0c;这个不是可执行文件&#xff0c;是一个解压缩的开头。 安装好bandiZip后&#xff0c;右键这个.exe文件 点击打开就是开始解压了&#xff1a; 最后解压后是这些。然后一个个再次解压.

微机接口课设——基于Proteus和8086的打地鼠设计(8255、8253、8259)Proteus中Unknown 1-byte opcode / Unknown 2-byte opcode错误

原理图设计 汇编代码 ; I/O 端口地址定义 IOY0 EQU 0600H IOY1 EQU 0640H IOY2 EQU 0680HMY8255_A EQU IOY000H*2 ; 8255 A 口端口地址 MY8255_B EQU IOY001H*2 ; 8255 B 口端口地址 MY8255_C EQU IOY002H*2 ; 8255 C 口端口地址 MY8255_MODE EQU IOY003H*2 ; …

密码学精简版

密码学是数学上的一个分支&#xff0c;同时也是计算机安全方向上很重要的一个原理&#xff0c;设置密码的目的是保证信息的机密性、完整性和不可抵赖性&#xff0c;安全方向上另外的功能——可用性则无法保证。 密码的发展也已由来已久&#xff0c;最早的密码可追溯到罗马时期…

在 macOS 上,你可以使用系统自带的 终端(Terminal) 工具,通过 SSH 协议远程连接服务器

文章目录 1. 打开终端2. 使用 SSH 命令连接服务器3. 输入密码4. 连接成功5. 使用密钥登录&#xff08;可选&#xff09;6. 退出 SSH 连接7. 其他常用 SSH 选项8. 常见问题排查问题 1&#xff1a;连接超时问题 2&#xff1a;权限被拒绝&#xff08;Permission denied&#xff09…

【书籍连载】《软件测试架构实践与精准测试》| 有关软件测试模型的调查结果

各位软件领域的精英们&#xff0c;今天小编邀请你继续深入学习《软件测试架构实践与精准测试》。 《软件测试架构实践与精准测试》是作者李龙&#xff08;安畅检测首席技术专家&#xff09;基于软件测试“川模型”的著作。本书结合作者首次提出的软件测试新的模型“川模型”测试…

软件工程大作业——图书管理系统/图书个性化推荐与实现系统

目录 1 绪论 1.1研究背景 1.2研究现状 1.3研究内容 2 系统关键技术 2.1 Spring Boot框架 2.2 JAVA技术 2.3 MYSQL数据库 2.4 B/S结构 3 系统分析 3.1 可行性分析 3.1.1 技术可行性 3.1.2经济可行性 3.1.3操作可行性 3.2 系统性能分析 3.3 系统功能分析 3.4系统流程分析 3.4.1登…

“AI智慧教学系统:开启个性化教育新时代

大家好&#xff0c;我是老王&#xff0c;一个在产品圈摸爬滚打多年的资深产品经理。今天&#xff0c;我想和大家聊聊一个最近特别火的概念——AI智慧教学系统。这东西听起来好像很高大上&#xff0c;但其实和我们每个人都息息相关&#xff0c;因为它关系到我们下一代的教育。 一…

vue实现平滑滚动到目标标签页

平滑滚动 <div class"tabs" ref"tabList"><div class"tab" v-for"(item, index) in 10":key"index" click"clickTab(index)"><div class"inside" :class"tabIndex index ? ins…

防御式CSS是一种编写CSS的方法,旨

1.防御式CSS 防御式CSS是一种编写CSS的方法&#xff0c;旨在提高样式的健壮性和可维护性。以下是一些实现防御式CSS的关键策略&#xff1a; 避免使用!important 尽量避免使用!important&#xff0c;因为它会破坏CSS的优先级规则&#xff0c;导致样式难以调试和维护。 使用具…

Python实现接口签名调用

目录: 1、第三方接口签名调用2、调用结果 1、第三方接口签名调用 import json import requests import hashlib import time import hmac access_key xxxxxxxxxxxxxxx secret_key xxxxxxxxxxxxxxx # 应用信息 def _wps4_sig(method, url, date, body): print(body)if bod…

【Hackthebox 中英 Write-Up】Web Request | 分析 HTTP 请求和响应

欢迎来到我的writeup分享&#xff01;我希望大家不要只关注结果或答案&#xff0c;而是通过耐心阅读&#xff0c;尝试逆向工程理解背后的运作原理。在这里&#xff0c;你不仅能找到解题的思路&#xff0c;还能学到更多与Hack The Box等平台相关的技术和技巧&#xff0c;期待与你…

物联网控制期末复习

第3章 物联网控制系统的过程通道设计 3.1 模拟量输出通道 3.1.1单模拟量输出通道的构成 计算机控制系统的模拟量输出通道将计算机产生的数字控制信号转换为模拟信号&#xff08;电压或电流&#xff09;作用于执行机构&#xff0c;以实现对被控对象的控制。 多D/A结构&#…

探索Wiki:开源知识管理平台及其私有化部署

在如今的信息时代&#xff0c;企业和团队的知识管理变得愈发重要。如何有效地存储、整理、共享和协作&#xff0c;是提高团队效率和创新能力的关键因素之一。今天&#xff0c;我要为大家介绍一款非常有用的github上开源知识管理工具——Wiki&#xff0c;并分享它的私有化部署方…

PDF文件提示-文档无法打印-的解决办法

背景信息 下载了几个签名的PDF文件&#xff0c;想要打印纸质版&#xff0c;结果打印时 Adobe Acrobat Reader 提示【文档无法打印】: 解决办法 网上的方案是使用老版本的PDF阅读器&#xff0c; 因为无法打印只是一个标识而已。 PDF文件不能打印的五种解决方案-zhihu 这些方…

快速上手LangChain(三)构建检索增强生成(RAG)应用

文章目录 快速上手LangChain(三)构建检索增强生成(RAG)应用概述索引阿里嵌入模型 Embedding检索和生成RAG应用(demo:根据我的博客主页,分析一下我的技术栈)快速上手LangChain(三)构建检索增强生成(RAG)应用 langchain官方文档:https://python.langchain.ac.cn/do…