软件设计模式系列之七——原型模式

news2025/1/15 19:49:55

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;
    }

    @Override
    public 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 小结

原型模式是一种用于创建对象的设计模式,它通过克隆现有对象来创建新对象,从而提高性能、简化对象创建和支持动态配置对象的需求。原型模式在需要频繁创建对象,或者需要保护对象不受修改影响的情况下非常有用。

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

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

相关文章

大数据Flink(八十二):SQL语法的DDL:Create 子句

文章目录 SQL语法的DDL:Create 子句 一、建表语句

蓝桥杯打卡Day10

文章目录 最长ZigZag子序列最小面积子矩阵 一、最长ZigZag子序列IO链接 本题思路&#xff1a;本题是一道dp问题&#xff0c; 集合划分:只有一个a[i]或者倒数第二个元素是第j个数字并且需要是下降得到a[j]:g[j]1,状态计算f[i]max(f[i],g[j]1),这是第一种情况&#xff0c;还有一…

许可分析 license分析 第十四章

许可分析是指对软件许可证进行详细的分析和评估&#xff0c;以了解组织内部对软件许可的需求和使用情况。通过许可分析&#xff0c;可以帮助组织更好地管理和优化软件许可证的使用。以下是一些可能的许可分析方法和步骤&#xff1a; 软件许可证的软件定义&#xff1a;采用软件定…

Kotlin File FileTreeWalk walkTopDown onEnter onLeave

Kotlin File FileTreeWalk walkTopDown onEnter onLeave Python遍历文件目录os.walk_for subfolder in subfolders: print(foldername/_zhangphil的博客-CSDN博客import osfor folderName, subfolders, filenames in os.walk(rD:\code\vs_code): print(当前文件夹: folderName…

【Java 基础篇】深入理解Java递归:从小白到专家

在编程世界中&#xff0c;递归是一个经常被提及的概念。但对于初学者来说&#xff0c;它可能会感到有点神秘和复杂。本文将深入探讨Java中的递归&#xff0c;从基础概念开始&#xff0c;逐步深入&#xff0c;帮助你理解这个强大的编程工具。 什么是递归&#xff1f; 递归是一…

进程,线程,并发相关入门

进程与线程的简单理解 进程是一个独立的执行单元&#xff0c;它拥有自己的内存空间、文件句柄和系统资源.进程是操作系统层面的,每个应用运行就是一个进程.进程之间通常是隔离的&#xff0c;它们不能直接访问对方的内存空间&#xff0c;必须通过进程间通信&#xff08;IPC&…

java微服务项目整合skywalking链路追踪框架

skywalking官网网址&#xff1a;Apache SkyWalking 目录 1、安装skywalking 2、微服务接入skywalking 3、skywalking数据持久化 1、安装skywalking 下载skywalking&#xff0c;本篇文章使用的skywalking版本是8.5.0 Index of /dist/skywalkinghttps://archive.apache.org/…

Java中代理的实现方式

Java中代理的实现方式 1. 静态代理2. JDK动态代理3. CGLIB动态代理扩展 在Java中&#xff0c;有多种方式可以实现代理&#xff0c;包括&#xff1a; 静态代理&#xff1a; 创建一个代理类&#xff0c;实现与目标类相同的接口或继承与目标类相同的父类。在代理类中持有一个目标类…

6.2、Flink数据写入到Kafka

目录 1、添加POM依赖 2、API使用说明 3、序列化器 3.1 使用预定义的序列化器 3.2 使用自定义的序列化器 4、容错保证级别 4.1 至少一次 的配置 4.2 精确一次 的配置 5、这是一个完整的入门案例 1、添加POM依赖 Apache Flink 集成了通用的 Kafka 连接器&#xff0c;使…

Python:函数调用的实参

相关阅读 Python专栏https://blog.csdn.net/weixin_45791458/category_12403403.html 调用就是附带可能为空的一系列参数来执行一个可调用对象 &#xff08;例如函数&#xff09;&#xff0c;它的语法的BNF范式如下所示&#xff0c;有关BNF范式的规则&#xff0c;可以参考之前…

无涯教程-JavaScript - CEILING.MATH函数

描述 CEILING.MATH函数将数字四舍五入到最接近的整数或最接近的有效倍数。 Excel CEILING.MATH函数是Excel中的十五个舍入函数之一。 语法 CEILING.MATH (number, [significance], [mode])争论 Argument描述Required/OptionalNumberNumber must be less than 9.99E307 and …

运维自动化:提高效率的秘诀

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

从一到无穷大 #16 ByteSeries,思考内存时序数据库的必要性

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。 本作品 (李兆龙 博文, 由 李兆龙 创作)&#xff0c;由 李兆龙 确认&#xff0c;转载请注明版权。 引言 在[3]中我基于Gorilla讨论了时序数据库设置cache的可行性&#xff0c;最后得出结论&…

许可分析 license分析 第十章

许可分析是指对软件许可证进行详细的分析和评估&#xff0c;以了解组织内部对软件许可的需求和使用情况。通过许可分析&#xff0c;可以帮助组织更好地管理和优化软件许可证的使用。以下是一些可能的许可分析方法和步骤&#xff1a; 软件许可证供应商管理&#xff1a;评估和管理…

CSS 浮动布局

浮动的设计初衷 float: left/right/both;浮动是网页布局最古老的方式。 浮动一开始并不是为了网页布局而设计&#xff0c;它的初衷是将一个元素拉到一侧&#xff0c;这样文档流就能够包围它。 常见的用途是文本环绕图片&#xff1a; 浮动元素会被移出正常文档流&#xff0c;…

Redis集群总结

Redis&#xff0c;作为一款开源的、内存中的数据结构存储系统&#xff0c;以其出色的性能和丰富的数据结构在业界赢得了广泛的认可。然而&#xff0c;当我们面临大量数据和高并发请求时&#xff0c;单个 Redis 实例可能无法满足我们的需求。这时&#xff0c;我们就需要使用到 R…

Kubernetes实践:从入门到实践

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

认识Git的工作区、暂存区与版本库

使用 git init 命令在 gitcode 文件夹下创建如下图所示的Git仓库。现在思考这样一个问题&#xff1a;gitcode目录下创建的README文件可以直接被git管理和追踪吗&#xff1f; 答案是否定的&#xff0c;因为只有 Git 本地仓库中的文件才可以被版本控制。什么&#xff1f;难道当前…

内网隧道代理技术(二十七)之 DNS隧道介绍

DNS隧道介绍 DNS协议介绍 域名系统(Domain Name System,缩写:DNS)是互联网的一项服务。它作为将域名和IP地址相互映射的一个分布式数据库,能够使人更方便地访问互联网。DNS使用TCP和UDP端口53。当前,对于每一级域名长度的限制是63个字符,域名总长度则不能超过253个字符…

第二篇------Virtual I/O Device (VIRTIO) Version 1.1

上篇文章&#xff1a;https://blog.csdn.net/Phoenix_zxk/article/details/132917657 篇幅太大&#xff0c;所以分开写&#xff0c;接下来续上 4.3.3.2.1 设备要求&#xff1a;Guest->Host 通知 设备必须忽略 GPR2 的位 0-31&#xff08;从左边数&#xff09;。这样可以使…