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

news2025/1/6 6:19:54

在这里插入图片描述

适配器模式(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. 性能损耗:适配器在运行时需进行接口转换、数据格式调整等操作,相较于直接调用原生兼容接口,可能会有一定的性能损失,不过在多数非性能敏感场景下可接受。

在这里插入图片描述

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

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

相关文章

二维码文件在线管理系统-收费版

需求背景 如果大家想要在网上管理自己的文件&#xff0c;而且需要生成二维码&#xff0c;下面推荐【草料二维码】&#xff0c;这个系统很好。特别适合那些制造业&#xff0c;实体业的使用手册&#xff0c;你可以生成一个二维码&#xff0c;贴在设备上&#xff0c;然后这个二维码…

MySQL8安装与卸载

1.下载mysql MySQL :: Download MySQL Community Serverhttps://dev.mysql.com/downloads/mysql/ 2.解压mysql安装包 解压到自己定义的目录&#xff0c;这里解压就是安装&#xff0c;解压后的路径不要有空格和中文。 3.配置环境变量 配置环境变量可以方便电脑在任何的路径…

数据挖掘——关联规则挖掘

数据挖掘——关联数据挖掘 关联数据挖掘关联规则关联规则挖掘问题&#xff1a;具体挖掘过程Apriori 产生关联规则 关联数据挖掘 关联分析用于发现隐藏在大型数据集中的令人感兴趣的联系&#xff0c;所发现的模式通常用关联规则或频繁项集的形式表示。 关联规则反映一个事物与…

【74HC192减法24/20/72进制】2022-5-17

缘由用74ls192设计一个72进制的减法计数器&#xff0c;需要有逻辑电路图-硬件开发-CSDN问答

Samsung手机首次主要采用竞对Micron LPDDR5内存

根据韩国媒体《韩国先驱报》&#xff08;The Korea Herald&#xff09;的报道&#xff0c;即将在1月底发布的三星 Galaxy S25 系列智能手机将首次主要使用美光科技&#xff08;Micron Technology&#xff09;提供的移动DRAM&#xff0c;而非三星自家的产品。这一消息对于三星的…

Linux驱动开发学习准备(Linux内核源码添加到工程-Workspace)

Linux内核源码添加到VsCode工程 下载Linux-4.9.88源码&#xff1a; 没有处理同名文件的压缩包&#xff1a; https://pan.baidu.com/s/1yjIBXmxG9pwP0aOhW8VAVQ?pwde9cv 已把同名文件中以大写命名的文件加上_2后缀的压缩包&#xff1a; https://pan.baidu.com/s/1RIRRUllYFn2…

leetcode题目(3)

目录 1.加一 2.二进制求和 3.x的平方根 4.爬楼梯 5.颜色分类 6.二叉树的中序遍历 1.加一 https://leetcode.cn/problems/plus-one/ class Solution { public:vector<int> plusOne(vector<int>& digits) {int n digits.size();for(int i n -1;i>0;-…

vue3+Echarts+ts实现甘特图

项目场景&#xff1a; vue3Echartsts实现甘特图;发布任务 代码实现 封装ganttEcharts.vue <template><!-- Echarts 甘特图 --><div ref"progressChart" class"w100 h100"></div> </template> <script lang"ts&qu…

接受Header使用错Map类型,导致获取到的Header值不全

问题复现 在 Spring 中解析 Header 时&#xff0c;我们在多数场合中是直接按需解析的。例如&#xff0c;我们想使用一个名为 myHeaderName 的 Header&#xff0c;我们会书写代码如下&#xff1a;RequestMapping(path "/hi", method RequestMethod.GET) public Str…

GitHub的简单操作

引言 今天开始就要开始做项目了&#xff0c;上午是要把git搭好。搭的过程中遇到好多好多的问题。下面就说一下git的简单操作流程。我们是使用的GitHub,下面也就以这个为例了 一、GitHub账号的登录注册 https://github.com/ 通过这个网址可以来到GitHub首页 点击中间绿色的S…

【时时三省】(C语言基础)常见的动态内存错误

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ----CSDN 时时三省 对NULL指针的解引用操作 示例&#xff1a; malloc申请空间的时候它可能会失败 比如我申请一块非常大的空间 那么空间可能就会开辟失败 正常的话要写一个if&#xff08;p&#xff1d;&#x…

【51项目】51单片机自制小霸王游戏机

视频演示效果&#xff1a; 纳新作品——小霸王游戏机 目录&#xff1a; 目录 视频演示效果&#xff1a; 目录&#xff1a; 前言&#xff1a; 一、连接方式&#xff1a; 1.1 控制引脚 1.2. 显示模块 1.3. 定时器 1.4. 游戏逻辑与硬件结合 1.5. 中断处理 二、源码分析&#xff1a…

ESP32-S3遇见OpenAI:OpenAI官方发布ESP32嵌入式实时RTC SDK

目录 OpenAI RTC SDK简介应用场景详解智能家居控制系统个人健康助手教育玩具 技术亮点解析低功耗设计快速响应高精度RTC安全性保障开发者指南 最近&#xff0c;OpenAI官方发布了一款针对ESP32-S3的嵌入式实时RTC&#xff08;实时时钟&#xff09;SDK&#xff0c;这标志着ESP32-…

Elasticsearch:减少 Elastic 容器镜像中的 CVE(常见的漏洞和暴露)

作者&#xff1a;来自 Elastic Maxime Greau 在这篇博文中&#xff0c;我们将讨论如何通过在 Elastic 产品中切换到最小基础镜像并优化可扩展漏洞管理程序的工作流程来显著减少 Elastic 容器镜像中的常见漏洞和暴露 (Common Vulnerabilities and Exposures - CVEs)。 基于 Chai…

计算机网络 (21)网络层的几个重要概念

前言 计算机网络中的网络层是OSI&#xff08;开放系统互连&#xff09;模型中的第三层&#xff0c;也是TCP/IP模型中的第二层&#xff0c;它位于数据链路层和传输层之间&#xff0c;负责数据包从源主机到目的主机的路径选择和数据转发。 一、网络层的主要功能 路由选择&#xf…

LED背光驱动芯片RT9293应用电路

一&#xff09;简介&#xff1a; RT9293 是一款高频、异步的 Boost 升压型 LED 定电流驱动控制器&#xff0c;其工作原理如下&#xff1a; 1&#xff09;基本电路结构及原理 RT9293的主要功能为上图的Q1. Boost 电路核心原理&#xff1a;基于电感和电容的特性实现升压功能。当…

第四届计算机、人工智能与控制工程

第四届计算机、人工智能与控制工程 The 4th International Conference on Computer, Artificial Intelligence and Control Engineering 重要信息 大会官网&#xff1a;www.ic-caice.net 大会时间&#xff1a;2025年1月10-12日 大会地点&#xff1a;中国合肥 (安徽大学磬苑…

【Rust 学习笔记】Rust 基础数据类型介绍——指针、元组和布尔类型

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 博客内容主要围绕&#xff1a; 5G/6G协议讲解 高级C语言讲解 Rust语言讲解 文章目录 Rust 基础数据类型介绍——指针、元组和布尔类型一、元组类型…

YOLO系列的学习

YOLOV1全解 You Only Look Once&#xff0c;把检测问题转化成回归问题&#xff0c;一个CNN就搞定了&#xff01;&#xff01;&#xff01;效率高&#xff0c;可对视频进行实时检测&#xff0c;应用领域非常广&#xff0c;到V3的时被美国军方用于军事行动&#xff0c;作者出于某…

鸿蒙应用开发搬砖经验之—使用DevTools工具调试前端页面

环境说明&#xff1a; 系统环境&#xff1a;Mac mini M2 14.5 (23F79) 开发IDE&#xff1a;DevEco Studio 5.0.1 Release 配置步骤&#xff1a; 按着官方的指引来慢慢一步一步来&#xff0c;但前提是要配置好SDK的路径&#xff08;没有配置的话&#xff0c;可能先看下面的配…