【设计模式】深入理解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——开启大模型世界
什么是大语言模型? 大语言模型是一种人工智能模型,通常使用深度学习技术(如神经网络)来理解和生成人类语言。这些模型拥有非常多的参数,可以达到数十亿甚至更多,使得它们能够处理高度复杂的语言模式。 我…...

UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...

【JavaEE】-- HTTP
1. HTTP是什么? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议,HTTP是基于TCP协议的一种应用层协议。 应用层协议:是计算机网络协议栈中最高层的协议,它定义了运行在不同主机上…...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...
Java - Mysql数据类型对应
Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...
爬虫基础学习day2
# 爬虫设计领域 工商:企查查、天眼查短视频:抖音、快手、西瓜 ---> 飞瓜电商:京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空:抓取所有航空公司价格 ---> 去哪儿自媒体:采集自媒体数据进…...
Caliper 配置文件解析:config.yaml
Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...
大学生职业发展与就业创业指导教学评价
这里是引用 作为软工2203/2204班的学生,我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要,而您认真负责的教学态度,让课程的每一部分都充满了实用价值。 尤其让我…...

如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...

使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
Redis:现代应用开发的高效内存数据存储利器
一、Redis的起源与发展 Redis最初由意大利程序员Salvatore Sanfilippo在2009年开发,其初衷是为了满足他自己的一个项目需求,即需要一个高性能的键值存储系统来解决传统数据库在高并发场景下的性能瓶颈。随着项目的开源,Redis凭借其简单易用、…...