【设计模式】深入理解Python中的适配器模式(Adapter Pattern)
深入理解Python中的适配器模式(Adapter Pattern)
在软件开发中,常常会遇到需要让不兼容的类或接口协同工作的问题。适配器模式(Adapter Pattern)是一种结构型设计模式,通过提供一个包装器对象,将一个类的接口转换成客户端期望的另一种接口,从而解决类接口不兼容的问题。
本文将详细探讨适配器模式的定义、应用场景、实现步骤,以及如何在Python中实现该模式,并探讨其优缺点及扩展。
1. 什么是适配器模式?
适配器模式是一种结构型设计模式,它允许我们将一个类的接口转换成另一类的接口,使得原本由于接口不兼容而无法一起工作的类可以协同工作。适配器模式的核心思想是创建一个包装类,该类包装了现有的类,并通过包装的方式为客户端提供期望的接口。
适配器模式的角色
适配器模式主要包括以下几个角色:
- 目标接口(Target):客户端期望使用的接口或抽象类。
- 现有类(Adaptee):需要适配的现有类,它具有不兼容的接口。
- 适配器(Adapter):适配器类负责将
Adaptee的接口转换成Target的接口,从而使得客户端能够通过Target接口使用Adaptee的功能。
UML 类图表示
+--------------------+ +-------------------+
| Target | | Adaptee |
+--------------------+ +-------------------+
| +request() | | +specific_request()|
+--------------------+ +-------------------+▲ ▲ | || || |
+--------------------+ +-------------------+
| Adapter | | Client |
+--------------------+ +-------------------+
| +request() | | +use_target() |
| -adaptee: Adaptee | +-------------------+
+--------------------+
适配器模式的两种形式
- 对象适配器:通过组合的方式,适配器类包含一个被适配的类的实例。
- 类适配器:通过继承的方式,适配器类同时继承目标类和被适配类。
2. 适配器模式的应用场景
适配器模式适用于以下几种情况:
- 接口不兼容的类需要协同工作:当已有的类由于接口不兼容而无法直接与系统中的其他类协同工作时,适配器模式是理想的解决方案。
- 复用已有的类,而不改变其代码:当一个类的功能符合需求,但它的接口与现有的系统不兼容时,可以通过适配器模式进行复用,而不需要修改已有类的代码。
- 使用第三方库或API:如果一个外部库或API的接口不符合当前系统的需求,可以通过适配器将其封装为符合需求的接口。
实际应用场景
- 数据库驱动适配:当使用不同数据库时,适配器模式可以将不同数据库驱动的API接口转换为统一的数据库访问接口。
- 日志系统适配:当需要将不同的日志系统统一到一个接口时,可以通过适配器模式来适配不同的日志库。
- UI适配器:在跨平台UI开发中,不同平台的UI组件接口可能不同,适配器模式可以帮助封装不同的UI组件以提供统一的接口。
3. Python 实现适配器模式
接下来,我们将通过代码来演示如何在Python中实现适配器模式。我们会以一个例子展开:假设我们有一个 Adaptee 类,它具有一个不符合当前系统需求的方法 specific_request,我们需要通过适配器将它适配成 Target 类中的 request 方法。
3.1 对象适配器的实现
对象适配器通过组合的方式来适配不兼容的类。
定义现有类(Adaptee)
class Adaptee:def specific_request(self):return "Adaptee: Specific behavior"
定义目标接口(Target)
class Target:def request(self):pass
定义适配器类(Adapter)
适配器类将 Adaptee 的 specific_request() 方法转换为 Target 的 request() 方法。
class Adapter(Target):def __init__(self, adaptee: Adaptee):self.adaptee = adapteedef request(self):return f"Adapter: Translated {self.adaptee.specific_request()}"
客户端代码
客户端可以通过 Target 接口使用 Adaptee 的功能,而不需要直接访问 Adaptee 类。
def client_code(target: Target):print(target.request())# 使用适配器
adaptee = Adaptee()
adapter = Adapter(adaptee)
client_code(adapter)
输出:
Adapter: Translated Adaptee: Specific behavior
在这个例子中,客户端只需要使用 Target 接口,而适配器 Adapter 将 Adaptee 的接口转换为客户端期望的接口。
3.2 类适配器的实现
类适配器通过继承的方式适配 Adaptee 类和 Target 类。Python支持多重继承,因此我们可以通过继承 Adaptee 和 Target 来实现类适配器。
class ClassAdapter(Target, Adaptee):def request(self):return f"ClassAdapter: Translated {self.specific_request()}"# 使用类适配器
adapter = ClassAdapter()
client_code(adapter)
输出:
ClassAdapter: Translated Adaptee: Specific behavior
这种方式避免了组合的使用,适合在多重继承不会带来复杂性时使用。
4. 适配器模式的优缺点
优点
- 提高了类的复用性:适配器模式允许我们在不修改已有代码的情况下使用不兼容的类,从而提高代码的复用性。
- 遵循单一职责原则:通过适配器类处理接口的转换工作,使得每个类都专注于自身的职责。
- 提高了系统的灵活性:可以很容易地添加新的适配器来适配不同的类,使系统具有更高的可扩展性。
缺点
- 增加了系统的复杂性:使用适配器模式会增加额外的适配器类,这可能会使系统的结构更加复杂。
- 性能开销:适配器模式通过包装方式引入了一层额外的调用,会在一定程度上增加系统的性能开销,特别是在高频调用的场景中。
5. Python特性下的适配器模式改进
Python作为动态语言,提供了一些特性,可以简化适配器模式的实现,比如鸭子类型和装饰器。通过这些特性,可以使适配器模式的实现更加灵活。
使用鸭子类型简化适配器
Python中的鸭子类型(Duck Typing)允许我们不严格依赖类型检查,只要对象具有相应的方法就可以直接调用。因此,适配器模式在Python中可以变得更加简洁。
class Adaptee:def specific_request(self):return "Adaptee: Specific behavior"class Client:def request(self, obj):return obj.specific_request()adaptee = Adaptee()
client = Client()
print(client.request(adaptee))
在这个例子中,Client 类并不关心传入的对象是什么类型,只要它具有 specific_request 方法即可。这是一种基于行为的动态适配,不需要额外的适配器类。
使用装饰器动态适配
装饰器是Python中一种非常强大的功能,可以用来动态修改或扩展对象的行为。在适配器模式中,装饰器可以用来动态适配对象。
def adapter_decorator(func):def wrapper():return f"Adapter Decorator: Translated {func()}"return wrapperclass Adaptee:def specific_request(self):return "Adaptee: Specific behavior"adaptee = Adaptee()
adaptee.specific_request = adapter_decorator(adaptee.specific_request)
print(adaptee.specific_request())
输出:
Adapter Decorator: Translated Adaptee: Specific behavior
通过使用装饰器,我们可以动态
适配对象的方法,而无需显式定义适配器类。
6. 结论
适配器模式是一个非常有用的设计模式,尤其在需要将不兼容的类或接口组合使用的场景中。通过适配器,系统可以在不修改已有代码的情况下重用类,从而提高灵活性和可扩展性。
在Python中,适配器模式的实现可以通过对象适配、类适配、鸭子类型和装饰器等方式进行,具体选择哪种方式取决于项目的需求和复杂性。
适配器模式虽然增加了系统的结构复杂度,但在实际应用中,它有效地提高了代码的复用性和系统的扩展能力。如果你在项目中遇到了接口不兼容的问题,适配器模式可能就是你需要的解决方案。
相关文章:
【设计模式】深入理解Python中的适配器模式(Adapter Pattern)
深入理解Python中的适配器模式(Adapter Pattern) 在软件开发中,常常会遇到需要让不兼容的类或接口协同工作的问题。适配器模式(Adapter Pattern)是一种结构型设计模式,通过提供一个包装器对象,…...
RuoYi-Vue若依框架-后端设置不登陆访问(白名单)
找到SecurityConfig类 确认自己的需求 /*** anyRequest | 匹配所有请求路径* access | SpringEl表达式结果为true时可以访问* anonymous | 匿名可以访问* denyAll | 用户不能访问* fullyAuthenticated | 用户完全认证可…...
C语言初阶小练习2(三子棋小游戏的实现代码)
这是C语言小游戏三子棋的代码实现 test.c文件是用来测试的部分 game.h文件是用来声明我们说写出的函数 game.c文件是用来编写我们的功能实现函数部分 1.test.c #define _CRT_SECURE_NO_WARNINGS 1 #include"game.h" void menu() {printf("***************…...
金融行业合同管理如何利用AI技术进行风险预警?
2024年以来,金融行业的发展主线被锚定,强调了防风险的基调,尤其是系统性风险的防范。金融工作的重点在于实现六个强大:强大的货币、强大的中央银行、强大的金融机构、强大的国际金融中心、强大的金融监管、强大的金融人才队伍。这…...
世界数字农业盛宴与技术探索,25年3月聚焦世界灌溉科技大会
由中国农业节水和农村供水技术协会、中国农垦节水农业产业技术联盟、北京物联网智能技术应用协会、振威国际会展集团主办的“世界灌溉科技大会”、“第11届北京国际数字农业与灌溉技术博览会”,定于2025年3月31日至4月2日在北京国家会议中心举办。 作为世界三大灌溉…...
二百六十九、Kettle——ClickHouse清洗ODS层原始数据增量导入到DWD层表中
一、目的 清洗ClickHouse的ODS层原始数据,增量导入到DWD层表中 二、实施步骤 2.1 newtime select( select create_time from hurys_jw.dwd_statistics order by create_time desc limit 1) as create_time 2.2 替换NULL值 2.3 clickhouse输入 2.4 字段选择 2.5 …...
Maya---骨骼绑定
调节骨骼大小 回车键确认骨骼 FK子集跟父集走 IK子集不跟父集走 前视图中按shift键添加骨骼 清零、删除历史记录,创建新的物体...
携手并进,智驭教育!和鲸科技与智谱 AI 签署“101 数智领航计划”战略合作协议
近日,上海和今信息科技有限公司(以下简称“和鲸科技”)与北京智谱华章科技有限公司(以下简称“智谱 AI”)签署“101 数智领航计划”战略合作协议。双方将携手营造智能化学科教育与科研环境,提供多种大模型工…...
牛客周赛63
https://ac.nowcoder.com/acm/contest/91592 好数 简单的判断两位数,且十位等于个位 #include <bits/stdc.h> #define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); #define int long long using namespace std; using ll long long; using pii …...
git restore恢复删除文件
新版本 在 Git 2.23 版本之后,Git 引入了一个新的命令 git restore,用于简化文件恢复操作。可以用 git restore 来恢复误删除的文件。下面是详细的使用方法: 1. 恢复工作区中删除的文件(未提交) 如果文件已被删除&a…...
MacOS13虚拟机VMware Workstation Pro 16安装
资源 安装unlocker 安装虚拟机 低版本的还没有MacOS13选项,这也是我安装低版本虚拟机踩过的坑 找个教程安装就可以了 省略…自己去找找教程… 过程中我使用桥接是不行的,没有网络,后面重新下一步一步的选择默认的网络重装后就好了&am…...
docker 数据管理,数据持久化详解 一
docker镜像是分层设计的,镜像出只读,通过镜像启动的容器添加一层可读写的文件系统,用户写入的数据表都保存在这层中。 容器的数据分层目录 LowerDir:image 镜像层,即镜像本身,制度 UpperDir:容…...
【ios】使用TestFlight将app分发给测试人员(超详细)
我的环境: macos系统是Ventura 13.0 xcode是14.2(后面发现至少需要15版本的xcode才能上传app) 证书生成 可以通过xcode生成Distribution类型的证书,如果你已经有的话那就忽略,这个证书也是备案时所需的。 我是已…...
证件照小程序源码,前后端稳定运行
演示:证寸照制作 运行环境: Linux Nginx PHP >5.6 MySQL>5.6 安装步骤: 1.下载源码上传至你的服务器宝塔面板 2.直接添加站点选择源码目录,新建数据库 3.设置代码执行目录为/web 4.在浏览器中输入你的域名,会提示安装,填写…...
java白嫖同事的从身份证里面提取省市区地址详细信息的工具类代码
/*** author sunpeiyang* date 2024/10/21 16:35*/ Slf4j public class MiTaAddressExtractor {/*** 获取详细地址** param fullAddress 身份证完整地址*/public static String getDetailedAddress(String fullAddress) {String[] addressArrays spliceDetailedAddress(fullAd…...
计算机网络基本架构示例2
一、企业内部网络架构 在一个中型企业中,通常会有以下的网络架构: - 核心层:由高性能的核心交换机组成,负责快速转发大量数据。例如采用具有高带宽和冗余功能的三层交换机,确保整个网络的稳定运行。它连接着各个部门的…...
无人机之室内定位技术篇
无人机的室内定位技术是实现无人机在室内环境中精准导航和定位的关键技术。由于室内环境复杂,卫星导航系统(如GPS)无法提供有效的信号,因此需要依赖其他室内定位技术。 一、主要技术类型 基于视觉的定位技术 原理:利…...
在ubuntu20.04中输入不存在shell命令时,报错ModuleNotFoundError的解决方案
这个问题出现过好几次,每次都比较困扰,以下的解决方案比较适合: 当我输入ubuntu无法识别的命令的时候,正常来说应该提示类似于 command not found 之类的字眼,但是系统确报了如下错误: Traceback (most r…...
互联网语言 互联网开发 互联网架构
JAVA和PHP是两种广泛应用于互联网开发的编程语言,它们在多个维度上展现出显著的不同。 JAVA是一种面向对象的编程语言,以其严谨、高效的特性而著称。JAVA的语法结构复杂且规范,强调封装、继承和多态等面向对象原则,适合构建大型企…...
解决MybatisPlus updateById更新数据时将没传的数据也更新成了null
首先,MybatisPlus在调用自带的更新接口updateById时,如果没加任何配置,默认是不会将前端没传的数据也更新成null的。即MyBatisPlus不会更新传入实体中为null的字段,只会更新设置了不为null的值。 如果发现没传的也更新成null了的话…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...
Python|GIF 解析与构建(5):手搓截屏和帧率控制
目录 Python|GIF 解析与构建(5):手搓截屏和帧率控制 一、引言 二、技术实现:手搓截屏模块 2.1 核心原理 2.2 代码解析:ScreenshotData类 2.2.1 截图函数:capture_screen 三、技术实现&…...
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...
RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...
在rocky linux 9.5上在线安装 docker
前面是指南,后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...
【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)
骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术,它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton):由层级结构的骨头组成,类似于人体骨骼蒙皮 (Mesh Skinning):将模型网格顶点绑定到骨骼上,使骨骼移动…...
Android15默认授权浮窗权限
我们经常有那种需求,客户需要定制的apk集成在ROM中,并且默认授予其【显示在其他应用的上层】权限,也就是我们常说的浮窗权限,那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...
QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...
CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)
漏洞概览 漏洞名称:Apache Flink REST API 任意文件读取漏洞CVE编号:CVE-2020-17519CVSS评分:7.5影响版本:Apache Flink 1.11.0、1.11.1、1.11.2修复版本:≥ 1.11.3 或 ≥ 1.12.0漏洞类型:路径遍历&#x…...
