软件设计模式系列之七——原型模式
1 模式的定义
原型模式(Prototype Pattern)是一种创建型设计模式,其主要目的是通过复制现有对象来创建新对象,而不是使用构造函数。原型模式将对象的创建委托给原型对象,通过克隆(复制)来生成新对象,这种方式可以避免对象的重复初始化,提高性能,并使对象的创建更加灵活和动态。
原型模式的关键思想是通过复制已有对象的属性和状态来创建新的对象,这种方式避免了每次都使用构造函数初始化对象,特别适用于对象创建过程复杂、耗时或需要动态配置的情况。
2 举例说明
原型模式在日常生活中的一个常见示例是使用复印机来复制文件或文档。如果你需要复制一份文件,一般情况下不会手工重新编写该文件的每个字,而是使用复印机来制作副本。在这里,原文件充当原型,而复印机则是用于创建新文件副本的工具。使用复印机来复制文件,通过克隆原文件来创建新文件副本,从而节省时间和工作量。

还有一个例子就是在西游记中,孙悟空用自己的猴毛变成很多新的孙悟空,也可以看作孙悟空用自己做原型,拷贝出相同的猴子。
这些例子都有助于更好地理解原型模式的概念和应用。
3 结构
原型模式的结构包括以下要素:
抽象原型接口(Prototype):这是一个抽象接口或抽象类,它声明了一个克隆方法(通常命名为 clone() 或类似的名称)。这个方法用于复制当前对象并返回一个新的副本。所有具体原型类都必须实现这个接口或继承这个抽象类,以确保它们能够被克隆。
具体原型类(Concrete Prototype):这些是实际的对象类,它们实现了抽象原型接口,并提供了自己的克隆方法。具体原型类通常包含对象的属性和方法。当客户端需要创建新对象时,它们通过调用克隆方法来复制现有对象。
客户端(Client):客户端代码是使用原型模式的地方,它通过调用具体原型类的克隆方法来创建新对象。客户端不需要了解对象的具体构造方式,只需知道如何复制对象。
以下是原型模式的典型结构示意图

在这个结构中,抽象原型接口定义了克隆方法,具体原型类实现了克隆方法,并可以包含其他属性和方法。客户端代码通过克隆方法创建新对象,而不必关心对象的具体构造细节。这种结构使得对象的创建更加灵活和可维护。
4 实现步骤
实现原型模式的关键步骤包括以下几个:
创建抽象原型接口(Prototype):首先,创建一个抽象原型接口或抽象类,其中包含一个克隆方法(通常命名为 clone() 或类似的名称),用于复制当前对象并返回一个新的副本。这个接口将规范所有具体原型类必须实现的方法。
创建具体原型类(Concrete Prototype):对于每个需要被克隆的具体对象类型,创建一个具体原型类,它实现了抽象原型接口,并提供了自己的克隆方法。在克隆方法中,通常会创建一个新的对象,将当前对象的属性值复制给新对象,并返回新对象。
客户端使用原型对象:在客户端代码中,当需要创建新对象时,不直接使用构造函数,而是通过克隆已有的原型对象来创建新对象。客户端代码通常只需要知道如何调用原型对象的克隆方法,而无需了解对象的具体构造细节。
克隆方法的实现:在具体原型类中,克隆方法的具体实现取决于对象的类型和属性。如果对象包含引用类型的成员变量,需要考虑深度克隆以确保对象的所有状态都被正确复制。
测试和验证:在客户端代码中测试原型模式,确保克隆的对象与原始对象在属性和行为上一致。
5 代码实现
以下是一个通用的原型模式实现步骤示例(使用Java):
// 1. 创建抽象原型接口
interface Prototype {Prototype clone();
}// 2. 创建具体原型类
class ConcretePrototype implements Prototype {private String field;public ConcretePrototype(String field) {this.field = field;}@Overridepublic Prototype clone() {return new ConcretePrototype(this.field);}public void setField(String field) {this.field = field;}public String getField() {return field;}
}// 3. 客户端使用原型对象
public class Client {public static void main(String[] args) {// 创建原型对象Prototype original = new ConcretePrototype("Original Field");// 克隆原型对象来创建新对象Prototype clone = original.clone();// 验证新对象的属性与原始对象相同System.out.println("Original Field: " + original.getField());System.out.println("Clone Field: " + clone.getField());}
}
在这个示例中,抽象原型接口定义了克隆方法,具体原型类实现了该接口并提供了自己的克隆方法。客户端通过克隆方法创建新对象,验证新对象的属性与原始对象相同。这个示例展示了原型模式的基本实现步骤。
6 典型应用场景
原型模式在以下情况下是典型的应用场景:
需要创建对象的成本较高:当对象的创建和初始化成本较高时,原型模式可以显著提高性能。每次都使用构造函数创建对象可能会导致不必要的开销,因此通过复制已有对象来创建新对象更为高效。

在计算机游戏中,创建和初始化复杂的游戏角色可能需要大量时间和资源。如果游戏需要大量相似的角色,可以使用原型模式来复制现有角色,节省创建时间。
对象的属性变化频繁:当对象的属性需要经常变化,但你希望保持对象的初始状态作为基础,可以使用原型模式。这样,你可以创建一个原型对象,并根据需要克隆它来创建新的对象。
在图形设计工具中,用户可以创建和编辑图形对象,如图形文本框。原始对象可以充当原型,用户可以复制它来创建多个类似但具有不同文本内容的图形文本框。
动态配置对象:当对象的属性需要根据运行时配置或用户输入而变化时,原型模式很有用。你可以创建一个原型对象,然后根据需要修改其属性,而无需重新构建对象。
在网站创建工具中,用户可以创建网页并自定义颜色、字体、布局等属性。原始网页对象可以作为原型,用户可以克隆它并根据自己的需求修改属性。
保护性拷贝:原型模式可以用于创建对象的深拷贝,以保护原始对象免受外部修改的影响。这对于涉及敏感数据或状态的对象非常有用。
在安全敏感的应用程序中,用户身份验证对象可能包含用户的敏感信息。通过使用原型模式创建深拷贝,可以确保不会在外部修改原始对象的敏感数据。
总之,原型模式适用于需要创建对象的成本高、属性变化频繁、动态配置或需要保护性拷贝的场景。它提供了一种高效、灵活和可维护的方式来创建对象,并在许多领域中有广泛的应用。
7 优缺点
优点:
提高性能:避免了对象的重复初始化,提高了对象创建的效率。
简化对象创建:客户端代码可以通过克隆来创建新对象,无需了解对象的具体构造方式。
动态配置对象:允许在运行时动态配置对象的属性。
保护性拷贝:可以创建对象的深拷贝,保护原始对象免受修改的影响。
缺点:
需要实现克隆方法:每个具体原型类都需要实现克隆方法,这可能需要一些额外的工作。
浅克隆问题:默认的克隆操作是浅克隆,如果对象包含引用类型的成员变量,可能需要手动实现深克隆。
8 类似模式
在 Spring 框架中,使用了原型模式和单例模式来管理对象的创建和生命周期。
原型模式在 Spring 中的应用:
原型范围(Scope)的 Bean:在 Spring 中,你可以将一个 Bean 配置为原型范围,这意味着每次从 Spring 容器请求该 Bean 时,都会创建一个新的实例。这就是原型模式的应用,每次都克隆一个对象来创建新实例。这对于那些需要频繁创建新对象的场景非常有用,因为每个请求都会得到一个全新的 Bean 实例,而不会共享状态。
使用 prototype 作用域声明 Bean:在 Spring 的配置文件或使用注解时,你可以明确将 Bean 的作用域声明为 prototype,告诉 Spring 这个 Bean 是原型范围的,从而使用原型模式来创建对象。
<bean id="myBean" class="com.example.MyBean" scope="prototype">
</bean>
单例模式在 Spring 中的应用:
单例范围(Scope)的 Bean:默认情况下,Spring 中的 Bean 是单例范围的,这意味着 Spring 容器只会创建一个 Bean 的实例,并在整个应用程序中共享这个实例。这就是单例模式的应用,确保一个类只有一个实例。
使用 singleton 作用域声明 Bean:虽然默认是单例范围,但你也可以显式将 Bean 的作用域声明为 singleton,以明确表达这一点。
<bean id="myBean" class="com.example.MyBean" scope="singleton">
</bean>
在 Spring 中,原型模式和单例模式的选择取决于对象的生命周期和状态需求。如果你需要一个共享状态的单一实例,可以使用单例模式。如果需要每次请求都获得一个全新的对象实例,可以使用原型模式。Spring 提供了这两种范围的支持,以满足不同的业务需求。
9 小结
原型模式是一种用于创建对象的设计模式,它通过克隆现有对象来创建新对象,从而提高性能、简化对象创建和支持动态配置对象的需求。原型模式在需要频繁创建对象,或者需要保护对象不受修改影响的情况下非常有用。
相关文章:
软件设计模式系列之七——原型模式
1 模式的定义 原型模式(Prototype Pattern)是一种创建型设计模式,其主要目的是通过复制现有对象来创建新对象,而不是使用构造函数。原型模式将对象的创建委托给原型对象,通过克隆(复制)来生成新…...
PMP考试注意事项有哪些?
1. PMI明确规定:不允许考生使用自带文具,包括自带的笔、削笔刀、橡皮、笔袋、计算器和草稿纸等。 2. 本次考试考场内为每位考生配备2B铅笔、橡皮、计算器(若有需要)和草稿纸。如文具有缺损或考试过程中如需更换铅芯等,请向监考老师举手示意。…...
chartgpt+midjourney
chatGPT程序化生成故事 英文版脚本步骤 步骤一:在chatgpt中输入以下脚本,,标红为可变的文字,输入你想要的,目前是科幻,即科幻故事,你可以改为 fairy-tale,则写的是童话故事&#x…...
【SpringMVC】自定义注解
【SpringMVC】自定义注解 前言1. 什么是注解?2. 注解的用处3. 注解的原理1.1. Override1.2. SuppressWarnings 2. JDK元注解2.1. Retention2.2. Target2.3. Inherited2.4. Documented 3. 自定义注解3.1. 自定义注解的分类注解类 结语 自定义注解及其应用 前言 在J…...
【李沐深度学习笔记】数据操作实现
课程地址 数据操作实现p2 数据操作 首先导入PyTorch包(import torch),虽然叫PyTorch,但实际上要导入torch。 import torch张量 张量表示的是一个数值组成的数组,这个数组可以有很多个维度。 # 生成0-11的顺序序列构成的一维…...
【深度学习-注意力机制attention 在seq2seq中应用】
注意力机制 为什么需要注意力机制attention机制的架构总体设计一、attention本身实现评分函数 attention在网络模型的应用-Bahdanau 注意力加性注意力代码实现 为什么需要注意力机制 这是一个普通的seq2seq结构,用以实现机器对话,Encoder需要把一个输入的…...
详解混合类型文件(Polyglot文件)的应用生成与检测
1. 引入 混合类型文件(Polyglot文件),是指一个文件,既可以是合法的A类型,也可以是合法的B类型。 比如参考3中的文件,是一个html文件,可以用浏览器正常打开;它也是一个一个.jar文件&…...
QT之QTableView的简介
QT之QTableView的简介 QTableView 是 Qt 框架中的一个类,用于显示和编辑表格数据。它提供了一个灵活的模型/视图架构,允许用户以不同的方式显示和编辑数据。 以下是 QTableView 的一些常用函数及其用法: 1)QTableView(QWidget *pa…...
学习记忆——宫殿篇——记忆宫殿——记忆桩——知识讲解
类比 假设这些桩子好比不同的交通工具,每一种交通工具都可以助我们到达目的地,那举现在就根据你的时间以及现实情况,选择最合适自己的交通工具即可,重点在于你要熟悉每种交通工具的用途不区别。桩子也是如此,把所有的桩…...
Python lambda匿名函数
视频版教程 Python3零基础7天入门实战视频教程 前面我们所学的函数定义,都是有函数名的。 我们现在学的lambda函数是没有名称的,也就是匿名函数。 我们在只需要一次性使用的函数的时候,就可以用lambda匿名函数,简单方便快捷。 …...
成绩统计(蓝桥杯)
成绩统计 题目描述 小蓝给学生们组织了一场考试,卷面总分为 100 分,每个学生的得分都是一个 0 到 100 的整数。 如果得分至少是 60 分,则称为及格。如果得分至少为 85 分,则称为优秀。 请计算及格率和优秀率,用百分数…...
ETL与ELT理解
ETL ETL( Extract-Transform-Load),用来描述将数据从来源端经过抽取(Extract)、转换(Transform)、加载(Load)至目的端的过程。ETL模式适用于小数据量集。如果在转换过程…...
IntelliJ IDEA 2023 年下载、安装教程、好用插件推荐
文章目录 下载与安装IDEA常用插件推荐Alibaba Java Coding Guidelines(阿里巴巴Java开发规约)Key Promoter X(IDEA快捷键提示)Translation(翻译插件)Save Actions(优化保存插件)Codo…...
下载HTMLTestRunner并修改
目录 一. 下载HTMLTestRunner 二. 修改HTMLTestRunner 1. 修改内容 2. 修改原因 一. 下载HTMLTestRunner 下载报告模板地址:http://tungwaiyip.info/software/HTMLTestRunner.html 下载模块: 二. 修改HTMLTestRunner 将修改后的模块放到python安装目录下的..…...
C#回调函数学习1
回调函数(Callback Function)是一种函数指针,它指向的是由用户自己定义的回调函数。我们将这个回调函数的指针作为参数传递给另外一个函数,在这个函数工作完成后,它将通过这个回调函数的指针来回调通知调用者处理结果。…...
leetcode 232 用栈实现队列
请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty): 实现 MyQueue 类: void push(int x) 将元素 x 推到队列的末尾int pop() 从队列的开头移除并返回元素int peek() 返回队列开头…...
element UI表单验证,自定义验证规则
validator 可以为指定字段自定义验证函数——这就相当于把前边配置的东西用js按照以前的方式编写验证逻辑了。虽然麻烦点,但是能实现比较复杂的业务逻辑判断。 <el-form-itemlabel"中奖概率"prop"rate":rules"[{ required: true, mes…...
redis 主存复制
1. 前言 Redis的持久化机制,它很好的解决了单台Redis服务器由于意外情况导致Redis服务器进程退出或者Redis服务器宕机而造成的数据丢失问题。 在一定程度上保证了数据的安全性,即便是服务器宕机的情况下,也可以保证数据的丢失非常少。 通常…...
Unity Shader顶点数据疑问
1)Unity Shader顶点数据疑问 2)Unity 2018发布在iOS 16.3偶尔出现画面不动的问题 3)安卓游戏启动后提示“应用程序异常” 这是第352篇UWA技术知识分享的推送,精选了UWA社区的热门话题,涵盖了UWA问答、社区帖子等技术知…...
java写一个用于生成雪花id的工具类
我们创建一个类 叫 SnowflakeIdGenerator 作为生成雪花id的工具类 然后 编写代码如下 public class SnowflakeIdGenerator {private static final long START_TIMESTAMP 1609459200000L; // 设置起始时间戳,可以根据需要进行调整private static final long WORKER…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...
【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...
蓝桥杯 2024 15届国赛 A组 儿童节快乐
P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡,轻快的音乐在耳边持续回荡,小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下,六一来了。 今天是六一儿童节,小蓝老师为了让大家在节…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...
Java 加密常用的各种算法及其选择
在数字化时代,数据安全至关重要,Java 作为广泛应用的编程语言,提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景,有助于开发者在不同的业务需求中做出正确的选择。 一、对称加密算法…...
【配置 YOLOX 用于按目录分类的图片数据集】
现在的图标点选越来越多,如何一步解决,采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集(每个目录代表一个类别,目录下是该类别的所有图片),你需要进行以下配置步骤&#x…...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...
【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...
