设计模式——适配器模式(结构型)

news2024/9/23 7:34:04

引言

适配器模式是一种结构型设计模式, 它能使接口不兼容的对象能够相互合作。

 问题

假如你正在开发一款股票市场监测程序, 它会从不同来源下载 XML 格式的股票数据, 然后向用户呈现出美观的图表。

在开发过程中, 你决定在程序中整合一个第三方智能分析函数库。 但是遇到了一个问题, 那就是分析函数库只兼容 JSON 格式的数据。

你可以修改程序库来支持 XML。 但是, 这可能需要修改部分依赖该程序库的现有代码。 甚至还有更糟糕的情况, 你可能根本没有程序库的源代码, 从而无法对其进行修改。

解决方案

你可以创建一个适配器。 这是一个特殊的对象, 能够转换对象接口, 使其能与其他对象进行交互。

适配器模式通过封装对象将复杂的转换过程隐藏于幕后。 被封装的对象甚至察觉不到适配器的存在。 例如, 你可以使用一个将所有数据转换为英制单位 (如英尺和英里) 的适配器封装运行于米和千米单位制中的对象。

适配器不仅可以转换不同格式的数据, 其还有助于采用不同接口的对象之间的合作。 它的运作方式如下:

  1. 适配器实现与其中一个现有对象兼容的接口。
  2. 现有对象可以使用该接口安全地调用适配器方法。
  3. 适配器方法被调用后将以另一个对象兼容的格式和顺序将请求传递给该对象。

有时你甚至可以创建一个双向适配器来实现双向转换调用。

让我们回到股票市场程序。 为了解决数据格式不兼容的问题, 你可以为分析函数库中的每个类创建将 XML 转换为 JSON 格式的适配器, 然后让客户端仅通过这些适配器来与函数库进行交流。 当某个适配器被调用时, 它会将传入的 XML 数据转换为 JSON 结构, 并将其传递给被封装分析对象的相应方法。

真实世界类比

如果你是第一次从美国到欧洲旅行, 那么在给笔记本充电时可能会大吃一惊。 不同国家的电源插头和插座标准不同。 美国插头和德国插座不匹配。 同时提供美国标准插座和欧洲标准插头的电源适配器可以解决你的难题。 

适配器模式结构

对象适配器

实现时使用了构成原则: 适配器实现了其中一个对象的接口, 并对另一个对象进行封装。 所有流行的编程语言都可以实现适配器。

  1. 客户端 (Client) 是包含当前程序业务逻辑的类。

  2. 客户端接口 (Client Interface) 描述了其他类与客户端代码合作时必须遵循的协议。

  3. 服务 (Service) 中有一些功能类 (通常来自第三方或遗留系统)。 客户端与其接口不兼容, 因此无法直接调用其功能。

  4. 适配器 (Adapter) 是一个可以同时与客户端和服务交互的类: 它在实现客户端接口的同时封装了服务对象。 适配器接受客户端通过适配器接口发起的调用, 并将其转换为适用于被封装服务对象的调用。

  5. 客户端代码只需通过接口与适配器交互即可, 无需与具体的适配器类耦合。 因此, 你可以向程序中添加新类型的适配器而无需修改已有代码。 这在服务类的接口被更改或替换时很有用: 你无需修改客户端代码就可以创建新的适配器类。

类适配器

这一实现使用了继承机制: 适配器同时继承两个对象的接口。 请注意, 这种方式仅能在支持多重继承的编程语言中实现, 例如 C++。

  1. 类适配器不需要封装任何对象, 因为它同时继承了客户端和服务的行为。 适配功能在重写的方法中完成。 最后生成的适配器可替代已有的客户端类进行使用。

伪代码

下列适配器模式演示基于经典的 “方钉和圆孔” 问题。

适配器假扮成一个圆钉 (Round­Peg), 其半径等于方钉 (Square­Peg) 横截面对角线的一半 (即能够容纳方钉的最小外接圆的半径)。

// 假设你有两个接口相互兼容的类:圆孔(Round­Hole)和圆钉(Round­Peg)。
class RoundHole is
    constructor RoundHole(radius) { …… }

    method getRadius() is
        // 返回孔的半径。

    method fits(peg: RoundPeg) is
        return this.getRadius() >= peg.getRadius()

class RoundPeg is
    constructor RoundPeg(radius) { …… }

    method getRadius() is
        // 返回钉子的半径。


// 但还有一个不兼容的类:方钉(Square­Peg)。
class SquarePeg is
    constructor SquarePeg(width) { …… }

    method getWidth() is
        // 返回方钉的宽度。


// 适配器类让你能够将方钉放入圆孔中。它会对 RoundPeg 类进行扩展,以接收适
// 配器对象作为圆钉。
class SquarePegAdapter extends RoundPeg is
    // 在实际情况中,适配器中会包含一个 SquarePeg 类的实例。
    private field peg: SquarePeg

    constructor SquarePegAdapter(peg: SquarePeg) is
        this.peg = peg

    method getRadius() is
        // 适配器会假扮为一个圆钉,其半径刚好能与适配器实际封装的方钉搭配
        // 起来。
        return peg.getWidth() * Math.sqrt(2) / 2


// 客户端代码中的某个位置。
hole = new RoundHole(5)
rpeg = new RoundPeg(5)
hole.fits(rpeg) // true

small_sqpeg = new SquarePeg(5)
large_sqpeg = new SquarePeg(10)
hole.fits(small_sqpeg) // 此处无法编译(类型不一致)。

small_sqpeg_adapter = new SquarePegAdapter(small_sqpeg)
large_sqpeg_adapter = new SquarePegAdapter(large_sqpeg)
hole.fits(small_sqpeg_adapter) // true
hole.fits(large_sqpeg_adapter) // false

适配器模式适合应用场景

 当你希望使用某个类, 但是其接口与其他代码不兼容时, 可以使用适配器类。

 适配器模式允许你创建一个中间层类, 其可作为代码与遗留类、 第三方类或提供怪异接口的类之间的转换器。

 如果您需要复用这样一些类, 他们处于同一个继承体系, 并且他们又有了额外的一些共同的方法, 但是这些共同的方法不是所有在这一继承体系中的子类所具有的共性。

 你可以扩展每个子类, 将缺少的功能添加到新的子类中。 但是, 你必须在所有新子类中重复添加这些代码, 这样会使得代码有坏味道。

将缺失功能添加到一个适配器类中是一种优雅得多的解决方案。 然后你可以将缺少功能的对象封装在适配器中, 从而动态地获取所需功能。 如要这一点正常运作, 目标类必须要有通用接口, 适配器的成员变量应当遵循该通用接口。 这种方式同装饰模式非常相似。

 实现方式

  1. 确保至少有两个类的接口不兼容:

    • 一个无法修改 (通常是第三方、 遗留系统或者存在众多已有依赖的类) 的功能性服务类。
    • 一个或多个将受益于使用服务类的客户端类。
  2. 声明客户端接口, 描述客户端如何与服务交互。

  3. 创建遵循客户端接口的适配器类。 所有方法暂时都为空。

  4. 在适配器类中添加一个成员变量用于保存对于服务对象的引用。 通常情况下会通过构造函数对该成员变量进行初始化, 但有时在调用其方法时将该变量传递给适配器会更方便。

  5. 依次实现适配器类客户端接口的所有方法。 适配器会将实际工作委派给服务对象, 自身只负责接口或数据格式的转换。

  6. 客户端必须通过客户端接口使用适配器。 这样一来, 你就可以在不影响客户端代码的情况下修改或扩展适配器。

 适配器模式优缺点

  •  单一职责原则你可以将接口或数据转换代码从程序主要业务逻辑中分离。
  •  开闭原则。 只要客户端代码通过客户端接口与适配器进行交互, 你就能在不修改现有客户端代码的情况下在程序中添加新类型的适配器。
  •  代码整体复杂度增加, 因为你需要新增一系列接口和类。 有时直接更改服务类使其与其他代码兼容会更简单。

 与其他模式的关系

  • 桥接模式通常会于开发前期进行设计, 使你能够将程序的各个部分独立开来以便开发。 另一方面, 适配器模式通常在已有程序中使用, 让相互不兼容的类能很好地合作。

  • 适配器可以对已有对象的接口进行修改, 装饰模式则能在不改变对象接口的前提下强化对象功能。 此外, 装饰还支持递归组合, 适配器则无法实现。

  • 适配器能为被封装对象提供不同的接口, 代理模式能为对象提供相同的接口, 装饰则能为对象提供加强的接口。

  • 外观模式为现有对象定义了一个新接口, 适配器则会试图运用已有的接口。 适配器通常只封装一个对象, 外观通常会作用于整个对象子系统上。

  • 桥接、状态模式和策略模式(在某种程度上包括适配器) 模式的接口非常相似。 实际上, 它们都基于组合模式——即将工作委派给其他对象, 不过也各自解决了不同的问题。 模式并不只是以特定方式组织代码的配方, 你还可以使用它们来和其他开发者讨论模式所解决的问题。

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

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

相关文章

使用qt实现四则运算计算机项目

这是我们要包含的头文件 #include <QWidget> #include<QStack> #include<string.h> #include<string> 这是我在ui界面创建的计算机基础框架。 接下来要实现按住每个按钮在白框内显示&#xff1b; 因此我们要定义一个QString 类型的变量 QString e…

nginx服务前端访问查看无响应的问题

问题 nginx 启动之后&#xff0c;前端访问无数据&#xff0c;F12 查看&#xff0c;提示挂起。 以为是配置问题&#xff0c;查看配置文件&#xff0c;未发现配置的有问题。 原因 通过查看配置文件&#xff0c;发现转发的服务地址为127.0.0.1&#xff0c;手动ping 127.0.0.1&a…

AI人工智能与云原生:创新科技的完美结合

人工智能&#xff08;AI&#xff09;是当今科技领域的热门话题&#xff0c;而云原生则是一种新兴的软件开发和部署模式。AI人工智能与云原生的结合&#xff0c;为现代技术创新提供了无限的可能性。本文将探讨AI与云原生的关系&#xff0c;并介绍其如何在实际应用中实现协同效应…

谷歌宣布向云计算客户开放 Gemini Pro,开发者可用其构建应用

12 月 14 日消息&#xff0c;美国时间周三&#xff0c;谷歌宣布了一系列升级的人工智能&#xff08;AI&#xff09;功能&#xff0c;旨在为其云计算客户提供更好的服务。这家科技巨头正试图赶上竞争对手&#xff0c;比如微软和 OpenAI&#xff0c;它们都在积极利用人工智能的热…

docker安装最新版SQL Server并还原备份的数据库

docker安装数据库 拉取微软官方最新版镜像 docker pull mcr.microsoft.com/mssql/server 拉去镜像并创建容器 docker run -e "ACCEPT_EULAY" -e "SA_PASSWORDsa_password_123456" -p 1433:1433 -v /opt/:/opt --name sqlserver -d mcr.microsoft.com/ms…

〖大前端 - 基础入门三大核心之JS篇(54)〗- 原型和原型链

说明&#xff1a;该文属于 大前端全栈架构白宝书专栏&#xff0c;目前阶段免费&#xff0c;如需要项目实战或者是体系化资源&#xff0c;文末名片加V&#xff01;作者&#xff1a;哈哥撩编程&#xff0c;十余年工作经验, 从事过全栈研发、产品经理等工作&#xff0c;目前在公司…

LOF基金跟股票一样吗?

LOF基金&#xff0c;全称为"上市型开放式基金"&#xff0c;是一种可以在上海证券交易所认购、申购、赎回及交易的开放式证券投资基金。投资者可以通过上海证券交易所场内证券经营机构或场外基金销售机构进行认购、申购和赎回基金份额。 LOF基金的特点是既可以像股票…

三层交换机原理与配置

文章目录 三层交换机原理与配置一、三层交换技术概述二、传统的 MLS三、基于CEF 的MLS1、转发信息库&#xff08;FIB&#xff09;2、邻接关系表3、工作原理&#xff1a; 四、三层交换机的配置1、三层交换机配置命令2、三层交换机配置步骤 三层交换机原理与配置 一、三层交换技…

微服务 Nacos服务注册与发现

一、Nacos 功能介绍 在微服务架构下&#xff0c;一个业务服务会被拆分成多个微服务&#xff0c;各个服务之间相互通信完成整体的功能。另外&#xff0c;为了避免单点故障&#xff0c;微服务都会采取集群方式的高可用部署&#xff0c;集群规模越大&#xff0c;性能也会越高&…

linux磁盘空间清理

查看磁盘使用情况 查看磁盘分区上可以使用的磁盘空间 $ df -h若要查看文件类型和block&#xff0c;使用下面的命令 $ df -T查看每个文件和目录的磁盘使用空间&#xff0c;也就是文件的大小。 $ sudo du -sh /* $ sudo du -h --max-depth1 /清理旧的 Snap 包版本以释放磁盘空…

智能优化算法应用:基于乌燕鸥算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于乌燕鸥算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于乌燕鸥算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.乌燕鸥算法4.实验参数设定5.算法结果6.参考文…

Yum仓库架构解析与搭建实践

1.Yum仓库搭建 1.1本地Yum仓库图解 1.2Linux本地仓库搭建 配置本地光盘镜像仓库 1&#xff09;挂载 [roothadoop101 ~]# mount -t iso996 /dev/cdrom/mnt 2&#xff09;查看 [rooothadoop101 ~] # df -h | |grep -i mnt /dev/sr0 4.6G 4.4G 3&#xf…

机器视觉【1】相机的成像(畸变)模型

零、前言 很久没写文章&#xff0c;简单唠一唠。 不知道巧合还是蜀道同归&#xff0c;部门领导设定了些研究课题&#xff0c;用于公司部门员工的超前发展&#xff0c;该课题是“2D to 3D的三维重建”&#xff0c;这一块刚好是我个人看中的一个大方向&#xff0c;所以就有了这…

使用Docker本地安装部署Draw.io绘图工具并实现远程访问协作办公

前言 提到流程图&#xff0c;大家第一时间可能会想到Visio&#xff0c;不可否认&#xff0c;VIsio确实是功能强大&#xff0c;但是软件为收费&#xff0c;并且因为其功能强大&#xff0c;导致安装需要很多的系统内存&#xff0c;并且是不可跨平台使用。所以&#xff0c;今天给…

docker小白第三天

docker小白第三天 docker为什么会比虚拟机快 1、docker有着比虚拟机更少的抽象层。不需要Hypervisor实现硬件资源虚拟化&#xff0c;运行在docker容器上的程序直接使用的都是实际物理机的硬件资源&#xff0c;因此在CPU、内存利用率上docker将会在效率上有明显优势。 2、dock…

CSS盒模型 盒子尺寸问题

什么是盒模型 CSS盒模型分为用来放置内容的content区域&#xff0c;用来调整内容和边框距离的padding区域。用来限制盒子边界的border区域以及用于调整盒子之间距离的margin区域。现在去掉margin&#xff0c;只考虑盒子内部的尺寸问题。 我们先来制作一个盒子。 <!DOCTYPE…

PTA✨C语言 求e的近似值

7-1 求e的近似值 分数 15 全屏浏览题目 切换布局 作者 C课程组 单位 浙江大学 自然常数 e 可以用级数 11/1!1/2!⋯1/n!⋯ 来近似计算。本题要求对给定的非负整数 n&#xff0c;求该级数的前 n1 项和。 输入格式: 输入第一行中给出非负整数 n&#xff08;≤1000&#xff0…

读书笔记-《数据结构与算法》-摘要6[快速排序]

快速排序 核心&#xff1a;快排是一种采用分治思想的排序算法&#xff0c;大致分为三个步骤。 定基准——首先随机选择一个元素最为基准划分区——所有比基准小的元素置于基准左侧&#xff0c;比基准大的元素置于右侧递归调用——递归地调用此切分过程 快排的实现与『归并排…

【图论】普利姆算法,最小生成树

一次加入一个节点到我们的最下生成树中。加入哪个&#xff1f;跟着下面的步骤走一遍你就会了。 1. 把第一个节点A添加进来 2. 看两条边<A,B>,<A,E>,一个长度是3&#xff0c;一个长度是4&#xff0c;把长度短的边的另一个节点添加进来&#xff0c;也就是B 3. 再看A,…

StatusBar、NavigationBar窗口显示在Activity下面之aosp14窗口类bug线索征集

背景&#xff1a; hi&#xff0c;粉丝朋友们&#xff1a; 从上次帮助国际学员解决了一个分屏有黑屏的bug后&#xff0c;相关blog和解决方法 https://blog.csdn.net/learnframework/article/details/134708393 解决方法看b站视频&#xff1a; https://www.bilibili.com/video/B…