【设计模式】深入理解Python中的原型设计模式
深入理解Python中的原型设计模式
在软件开发中,有时需要创建对象的过程非常复杂或者代价较高,而在同一类对象的实例之间有很多重复的属性。为了避免重复构造对象,提升性能和效率,原型设计模式(Prototype Pattern)应运而生。
原型模式是一种创建型设计模式,它允许我们通过复制现有对象来创建新的对象,而不是通过实例化类创建对象。这样不仅可以简化对象的创建过程,还能显著提高性能。
在这篇文章中,我们将全面深入探讨原型模式的定义、使用场景、Python实现方法及其优缺点。
1. 什么是原型设计模式?
原型设计模式的核心思想是:通过复制现有对象来创建新的对象。原型模式在某种程度上是一种浅拷贝或深拷贝技术,通过复制一个对象的所有属性而无需重新执行构造函数。
原型模式的核心角色
- 原型接口(Prototype Interface):定义一个可以克隆自身的接口,通常是通过
clone()方法来复制对象。 - 具体原型类(Concrete Prototype):实现
clone()方法,允许克隆自身。 - 客户端(Client):通过调用原型对象的
clone()方法来复制对象。
原型模式的UML表示
+-----------------+
| Prototype |
+-----------------+
| + clone() |
+-----------------+▲|
+-----------------+
| ConcretePrototype|
+-----------------+
| + clone() |
+-----------------+
- Prototype:定义了
clone()方法,用于复制对象。 - ConcretePrototype:具体原型类实现了
clone()方法,可以生成自身的拷贝。 - Client:客户端通过原型对象进行对象复制。
2. 原型模式的应用场景
原型模式适用于以下场景:
- 对象的创建成本高:当对象的创建代价昂贵,且对象构造过程非常复杂时,使用原型模式可以通过复制现有对象快速生成新的实例。
- 需要创建多个相似的对象:如果多个对象的结构相似,使用原型模式可以通过复制来创建多个相似的实例,而无需手动创建或配置每个实例。
- 需要避免依赖具体类:通过原型模式,客户端无需依赖具体类来创建对象,降低了耦合度。
- 实例化需要动态变化:在某些场景下,对象的结构和配置会动态改变,原型模式可以帮助通过复制动态生成具有不同配置的对象。
典型的应用场景
- 对象池(Object Pool):为了提高性能,很多系统会维护对象池,使用原型模式复制对象池中的现有对象来减少实例化开销。
- 游戏开发:在游戏中,有许多相似的角色、怪物或道具,使用原型模式可以快速生成这些对象。
- 配置管理:在需要不同配置但具有相似结构的系统中,可以通过原型模式快速复制并生成配置文件。
3. Python中的原型模式实现
3.1 原型接口与实现
在Python中,原型模式可以通过 copy 模块中的 copy() 和 deepcopy() 函数实现。Python中的拷贝分为浅拷贝和深拷贝,分别对对象的引用和实际值进行拷贝。
浅拷贝与深拷贝的区别
- 浅拷贝:创建一个新的对象,但不复制嵌套对象的内容。对原始对象中嵌套对象的修改会反映在拷贝对象中。
- 深拷贝:递归复制所有嵌套对象,创建一个完全独立的副本。
首先,我们定义一个Prototype类,使用clone()方法来实现对象的复制。
import copy
from abc import ABC, abstractmethod# 抽象原型类
class Prototype(ABC):@abstractmethoddef clone(self):pass
3.2 具体原型类
接下来,我们定义具体的原型类,继承Prototype类并实现clone()方法。我们使用 copy 模块来实现浅拷贝和深拷贝。
# 具体原型类
class ConcretePrototype1(Prototype):def __init__(self, value, nested_object):self.value = valueself.nested_object = nested_objectdef clone(self, deep=False):if deep:return copy.deepcopy(self)else:return copy.copy(self)def __str__(self):return f"ConcretePrototype1(value={self.value}, nested_object={self.nested_object})"# 嵌套对象
class NestedObject:def __init__(self, attribute):self.attribute = attributedef __str__(self):return f"NestedObject(attribute={self.attribute})"
3.3 客户端代码
在客户端代码中,我们创建一个ConcretePrototype1对象,并通过浅拷贝和深拷贝的方式生成副本。
# 创建原型对象
nested_obj = NestedObject("Initial Attribute")
prototype = ConcretePrototype1(10, nested_obj)# 浅拷贝
shallow_copy = prototype.clone(deep=False)
# 深拷贝
deep_copy = prototype.clone(deep=True)# 输出原型和副本的状态
print("Original Prototype:", prototype)
print("Shallow Copy:", shallow_copy)
print("Deep Copy:", deep_copy)# 修改嵌套对象的属性
nested_obj.attribute = "Modified Attribute"print("\nAfter modifying the nested object:")
print("Original Prototype:", prototype)
print("Shallow Copy:", shallow_copy)
print("Deep Copy:", deep_copy)
运行结果:
Original Prototype: ConcretePrototype1(value=10, nested_object=NestedObject(attribute=Initial Attribute))
Shallow Copy: ConcretePrototype1(value=10, nested_object=NestedObject(attribute=Initial Attribute))
Deep Copy: ConcretePrototype1(value=10, nested_object=NestedObject(attribute=Initial Attribute))After modifying the nested object:
Original Prototype: ConcretePrototype1(value=10, nested_object=NestedObject(attribute=Modified Attribute))
Shallow Copy: ConcretePrototype1(value=10, nested_object=NestedObject(attribute=Modified Attribute))
Deep Copy: ConcretePrototype1(value=10, nested_object=NestedObject(attribute=Initial Attribute))
如上所示,在修改了嵌套对象后,浅拷贝的副本中的嵌套对象也发生了变化,而深拷贝的副本保持不变。这是因为深拷贝递归复制了所有嵌套对象,确保副本是一个完全独立的对象。
4. 原型模式的优缺点
优点
- 提高性能:原型模式通过复制现有对象,避免了复杂对象的重新构造,特别适合那些构造过程代价高的对象。
- 动态对象创建:原型模式可以动态生成对象,而不依赖于具体类,实现更灵活的对象创建机制。
- 减少重复代码:通过克隆现有对象,可以避免重复编写对象创建代码,简化代码逻辑。
- 实现对象池:原型模式是实现对象池的有效手段,在池中维护一些已有对象,随时可以复制出来使用。
缺点
- 对象拷贝的复杂性:对于包含复杂结构或深层嵌套的对象,特别是有引用循环的对象,拷贝的实现可能会变得复杂,可能需要额外的处理来防止深拷贝出现性能问题。
- 潜在的对象状态问题:在一些情况下,复制对象可能会导致共享状态问题,特别是在浅拷贝时,多个对象可能会共享相同的引用,修改其中一个会影响其他对象。
5. 原型模式的改进
在Python中,由于语言的动态特性,我们可以通过定制__copy__() 和 __deepcopy__()方法,优化对象的拷贝行为。对于复杂对象结构,可以选择性地实现浅拷贝和深拷贝。
自定义 __copy__() 和 __deepcopy__()
class CustomPrototype:def __init__(self, name, data):self.name = nameself.data = datadef __copy__(self):print("Performing shallow copy")return CustomPrototype(self.name, self.data)def __deepcopy__(self, memo):print("Performing deep copy")new_data = copy.deepcopy(self.data, memo)return CustomPrototype(self.name, new_data)# 测试自定义拷贝行为
prototype = CustomPrototype("Original", {"key": "value"})
shallow = copy.copy(prototype)
deep = copy.deepcopy(prototype)
在自定义 __copy__() 和 __deepcopy__() 方法时,我们可以精细控制哪些部分需要拷贝,哪些部分保持引用。这种方式可以在提高性能的同时避免冗余操作。
6. 结论
原型模式是一种灵活且高效的设计模式,通过复制现有对象来创建新的对象,从而避免了复杂的对象构造过程。在Python中,利用其内置的 copy 模块,我们可以轻松实现浅拷贝和深拷贝的功能。
原型模式尤其适合需要创建大量相似对象的场景,如对象池、游戏开发和配置管理。虽然它可以提高性能和减少重复代码,但在使用时需要特别注意对象拷贝的深度问题,以避免共享状态引发的潜在问题。
通过本文的介绍,希望你能够对原型模式的定义、应用场景和Python中的实现有一个全面的理解,并能够在实际项目中灵活应用这一模式。
相关文章:
【设计模式】深入理解Python中的原型设计模式
深入理解Python中的原型设计模式 在软件开发中,有时需要创建对象的过程非常复杂或者代价较高,而在同一类对象的实例之间有很多重复的属性。为了避免重复构造对象,提升性能和效率,原型设计模式(Prototype Pattern&…...
Django CORS配置方案
参考 https://pypi.org/project/django-cors-headers/ 在setting.py中设置 INSTALLED_APPS [......corsheaders, #添加此行 ]MIDDLEWARE[......corsheaders.middleware.CorsMiddleware, #添加此行django.middleware.common.CommonMiddleware,#django.middleware.csrf.CsrfVi…...
2024年开放式耳机哪个牌子好?推荐最好的顶级开放式耳机品牌
在当下,开放式耳机逐渐成为众多消费者的新宠。与传统入耳式耳机相比,开放式耳机展现出诸多独特之处。它可以呈现出更清晰的音质效果,让用户有更美妙的听觉体验。在佩戴感上,开放式耳机更为舒适,不会给耳朵带来压迫感。…...
零基础读懂Stable Diffusion!
前言 一文搞懂Stable Diffusion是什么,怎么训练和使用,语义信息影响生成图片的过程。>>[][加入极市CV技术交流群,走在计算机视觉的最前沿] 前几个月AIGC可谓是大热了一把,各种高质量的生成图片层出不穷,而其中…...
Hash Join 和 Index Join工作原理和性能差异
在数据库查询中,Hash Join 和 Index Join 是两种常见的表连接策略。了解它们的工作原理和性能差异有助于设计高效的数据库查询。我们可以使用 Java 模拟这两种不同的连接方式,并进行性能对比。 1. Hash Join 和 Index Join 的概念: Hash Joi…...
Apifox简介及使用
Apifox 是一款集 API文档管理、接口调试、接口自动化测试 和 Mock 功能于一体的全功能工具,旨在为开发者和测试人员提供一个高效的一站式解决方案。它融合了 Postman、Swagger、JMeter 等工具的优势,能够极大地提升团队协作和 API 开发的效率。 在实际开…...
十、IPD 实施细节(产品设计与开发管理)
产品设计与开发管理 产品设计与开发管理是IPD(集成产品开发)实施过程中的核心环节。它确保从概念设计到最终产品的实现能够按照预定的质量、成本、进度目标顺利完成,并与市场需求、技术发展及企业战略保持一致。IPD强调产品设计与开发管理过程中跨职能团队的协作、流程的系…...
MySQL-13.DQL-聚合函数
一.DQL-分组查询 二.聚合函数 -- DQL:分组查询 -- 聚合函数 -- 1.统计该企业员工数量 count select count(id) from tb_emp; select count(job) from tb_emp;select count(A) from tb_emp; select count(*) from tb_emp;-- 2.统计该企业最早入职的员工 min select min(entr…...
为什么跟别人学习如何证明定理要远比使用定理更有意义
目录 背景 为什么跟别人学习 什么是高人,如何判断 高人定义 如何判断一个人的能力? 如何考量一个人的成就? 只知道使用定理的局限性 1. 缺乏灵活性和适应性 2. 无法创新或拓展新方法 3. 容易误用或误解定理 4. 难以推理和分析复杂问…...
Qt在Win,Mac和Linux的开机自启设置
Windows Windows 使用注册表来管理开机自启的应用程序。 void runWithSystem(const QString& name, const QString& path, bool autoRun) {QSetting reg("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", QSetting::NativeForma…...
spring boot热部署
使用热部署解决了每次都需要重新启动的问题,但不过热部署的在对于改动比较小时速度可能快一些,改动大的话尽量停止 1.使用热部署之前需要在pom.xml文件中导入依赖 <dependency><groupId>org.springframework.boot</groupId><artifa…...
网关与蓝牙网关有什么不同之处?
尽管蓝牙网关也属于网关的一种,但在实际应用和功能特性上,它们之间存在着显著的差异。接下来,我们将深入探讨蓝牙网关的独特之处,并与传统网关进行对比分析。 一、网关与蓝牙网关的共同之处 一对多配对能力:无论是网关…...
JAVA计算双十一多产品实付款优惠券的省钱方案
为了计算双十一期间多产品使用优惠券后的实付款省钱方案,我们需要一个更复杂的逻辑来处理优惠券的应用和叠加规则。以下是一个简化的Java示例,用于展示如何计算多种优惠券应用于多个产品后的实付款金额,并找出最省钱的方案。 首先࿰…...
零售行业的数字化营销转型之路
一方面,市场竞争激烈,电商平台、新兴品牌和跨界对手带来巨大压力。另一方面,消费者需求变化迅速,更加追求个性化、多元化和便捷化的购物体验,同时传统零售企业还面临着高成本压力,如租金、人力和库存等。 然…...
js的for in 和 for of的详解
for...in 和 for...of 是 JavaScript 中的两种循环结构,它们用于不同的场景,适用于不同的数据类型。下面将详细介绍它们的用法、区别以及适用场景。 1. for...in 循环 for...in 用于遍历对象的可枚举属性(包括继承的属性)。 语…...
前端工具函数库
流行的前端工具函数库 lodashlodash-es:用lodash-es代替lodashes-toolkit:https://www.npmjs.com/package/es-toolkitradash:https://github.com/sodiray/radash 补充信息: antd-mobile 已不再依赖 lodash, 淘汰 lo…...
Java程序设计:Spring boot(4)——Freemarker Thymeleaf视图技术集成
1 Freemarker 视图集成 SpringBoot 内部⽀持 Freemarker 视图技术的集成,并提供了⾃动化配置类 FreeMarkerAuto Configuration,借助⾃动化配置可以很⽅便的集成 Freemarker基础到 SpringBoot 环境中。这⾥借助⼊⻔项⽬引⼊ Freemarker 环境配置。 Start…...
JavaScript 第19章:Web Storage
在JavaScript中,Web存储(Web Storage)提供了一种在用户浏览器中持久化数据的方式。这里我们会探讨localStorage、sessionStorage以及IndexedDB,并提供一些简单的示例代码来展示它们的用法。 localStorage localStorage允许你在用…...
[山河2024] week2
官方WP出得很快。对照官的写下私的。大概出入不大,毕竟第2周。后边的才难。 Crypto E&R RSA因子分解题,把q的2进制反转后与p异或。关于异或的题很多,这个还真是头一回见,不过爆破方法还是一样的。 r_q int(bin(q)[2:][::…...
无限可能LangChain——开启大模型世界
什么是大语言模型? 大语言模型是一种人工智能模型,通常使用深度学习技术(如神经网络)来理解和生成人类语言。这些模型拥有非常多的参数,可以达到数十亿甚至更多,使得它们能够处理高度复杂的语言模式。 我…...
Xshell远程连接Kali(默认 | 私钥)Note版
前言:xshell远程连接,私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...
全球首个30米分辨率湿地数据集(2000—2022)
数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...
1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...
Python爬虫(一):爬虫伪装
一、网站防爬机制概述 在当今互联网环境中,具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类: 身份验证机制:直接将未经授权的爬虫阻挡在外反爬技术体系:通过各种技术手段增加爬虫获取数据的难度…...
Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...
Java入门学习详细版(一)
大家好,Java 学习是一个系统学习的过程,核心原则就是“理论 实践 坚持”,并且需循序渐进,不可过于着急,本篇文章推出的这份详细入门学习资料将带大家从零基础开始,逐步掌握 Java 的核心概念和编程技能。 …...
聊一聊接口测试的意义有哪些?
目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开,首…...
[ACTF2020 新生赛]Include 1(php://filter伪协议)
题目 做法 启动靶机,点进去 点进去 查看URL,有 ?fileflag.php说明存在文件包含,原理是php://filter 协议 当它与包含函数结合时,php://filter流会被当作php文件执行。 用php://filter加编码,能让PHP把文件内容…...
从零开始了解数据采集(二十八)——制造业数字孪生
近年来,我国的工业领域正经历一场前所未有的数字化变革,从“双碳目标”到工业互联网平台的推广,国家政策和市场需求共同推动了制造业的升级。在这场变革中,数字孪生技术成为备受关注的关键工具,它不仅让企业“看见”设…...
机器学习的数学基础:线性模型
线性模型 线性模型的基本形式为: f ( x ) ω T x b f\left(\boldsymbol{x}\right)\boldsymbol{\omega}^\text{T}\boldsymbol{x}b f(x)ωTxb 回归问题 利用最小二乘法,得到 ω \boldsymbol{\omega} ω和 b b b的参数估计$ \boldsymbol{\hat{\omega}}…...
