当前位置: 首页 > news >正文

原型模式——对象的克隆

1、简介

1.1、概述

可以通过一个原型对象克隆出多个一模一样的对象,该模式被称为原型模式。
在使用原型模式时,需要首先创建一个原型对象,再通过复制这个原型对象来创建更多同类型的对象。

1.2、定义

原型模式(Prototype Pattern):使用原型实例指定创建对象的种类,并且通过克隆这些原型创建新的对象。原型模式是一种对象创建型模式。

2、解析

2.1、示例

原型模式的工作原理很简单:将一个原型对象传给要发动创建的对象,这个要发动创建的对象通过请求原型对象克隆自己来实现创建过程。由于在软件系统中经常会遇到需要创建多个相同或者相似对象的情况,因此原型模式在真实开发中的使用频率还是非常高的。原型模式是一种“另类”的创建型模式,创建克隆对象的工厂就是原型类自身,工厂方法由克隆方法来实现。

需要注意的是,通过克隆方法所创建的对象是全新的对象,它们在内存中拥有新的地址。通常,对克隆所产生的对象进行的修改不会对原型对象造成任何影响,每个克隆对象都是相互独立的。通过不同的方式对克隆对象进行修改以后,可以得到一系列相似但不完全相同的对象。原型模式的结构如下图所示:

在这里插入图片描述
在原型模式结构图中包含以下3个角色:
(1)Prototype(抽象原型类):它是声明克隆方法的接口,是所有具体原型类的公共父类,可以是抽象类也可以是接口,甚至还可以是具体实现类。
(2)ConcretePrototype(具体原型类):它实现在抽象原型类中声明的克隆方法,在克隆方法中返回自己的一个克隆对象。(3)Client(客户类):让一个原型对象克隆自身从而创建一个新的对象,在客户类中只需要直接实例化或通过工厂方法等方式创建一个原型对象,再通过调用该对象的克隆方法即可得到多个相同的对象。由于客户类针对抽象原型类Prototype编程,因此用户可以根据需要选择具体原型类,系统具有较好的可扩展性,增加或更换具体原型类都很方便。

2.2、Java中常用的克隆对象方法

原型模式的核心在于如何实现克隆方法,下面将介绍两种在Java语言中常用的克隆实现方法。

1、通用实现方法

通用的克隆实现方法是在具体原型类的克隆方法中实例化一个与自身类型相同的对象并将其返回,并将相关的参数传入新创建的对象中,保证它们的成员变量相同。示意代码如下:

public class ConcretePrototype extends Prototype {// 成员变量private String attr;public String getAttr() {return attr;}public void setAttr(String attr) {this.attr = attr;}// 克隆方法public Prototype clone() {// 创建对象ConcretePrototype prototype = new ConcretePrototype();prototype.setAttr(this.attr);return prototype;}
}

在客户类中,只需要创建一个ConcretePrototype对象作为原型对象,然后调用其clone()方法即可得到对应的克隆对象,代码如下:

Prototype obj1= new ConcretePrototype();
obj1.setAttr(this.attr);
Prototype obj2=obj1.clone();

这种方法可作为原型模式的通用实现,它与编程语言特性无关,任何面向对象语言都可以使用这种形式来实现对原型的克隆。

2、java的clone()方法

所有的Java类都继承自java.lang.Object。事实上,Object类提供了一个clone()方法,可以将一个Java对象克隆一份。因此在Java中可以直接使用Object提供的clone()方法来实现对象的克隆,Java语言中的原型模式实现很简单。

需要注意的是能够实现克隆的Java类必须实现一个标识接口Cloneable,表示这个Java类支持被复制。如果一个类没有实现这个接口但是调用了clone()方法,Java编译器将抛出一个CloneNotSupportedException异常。代码如下:

public class ConcretePrototype implements Cloneable {...public Prototype clone() {Object object = null;try {object = super.clone();} catch (CloneNotSupportedException exception) {exception.printStackTrace();}return (Prototype) object;}...
}

在客户端创建原型对象和克隆对象也很简单,代码如下:

Prototype obj1 = new ConcretePrototype();
Prototype obj2 = obj1.clone;

一般而言,Java语言中的clone()方法满足:
(1)对任何对象x,都有x.clone()!=x,即克隆对象与原型对象不是同一个对象。(2)对任何对象x,都有x.clone().getClass()==x.getClass(),即克隆对象与原型对象的类型一样。
(3)如果对象x的equals()方法定义恰当,那么x.clone().equals(x)应该成立。为了获取对象的一份克隆,可以直接利用Object类的clone()方法,具体步骤如下:

  • 在派生类中覆盖基类的clone()方法,并声明为public。
  • 在派生类的clone()方法中,调用super.clone()。
  • 派生类需实现Cloneable接口。

2.3、java中的浅克隆和深克隆

浅克隆(Shallow Clone)和深克隆(Deep Clone)。在Java语言中,数据类型分为值类型(基本数据类型)和引用类型,值类型包括int、double、byte、boolean、char等简单数据类型,引用类型包括类、接口、数组等复杂类型。浅克隆和深克隆的主要区别在于是否支持引用类型的成员变量的复制。

1、浅克隆

在浅克隆中,如果原型对象的成员变量是值类型,将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。简单来说,在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有被复制,如下图所示。
在这里插入图片描述
在Java语言中,通过覆盖Object类的clone()方法可以实现浅克隆。

2、深克隆

在深克隆中,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象。简单来说,在深克隆中,除了对象本身被复制外,对象所包含的所有成员变量也将被复制。

在Java语言中,如果需要实现深克隆,可以通过序列化(Serialization)等方式来实现。序列化就是将对象写到流的过程,写到流中的对象是原有对象的一个复制品,而原对象仍然存在于内存中。通过序列化实现的复制不仅可以复制对象本身,而且可以复制其引用的成员对象,因此通过序列化将对象写到一个流中,再从流里将其读出来,可以实现深克隆。需要注意的是,能够实现序列化的对象其类必须实现Serializable接口,否则无法实现序列化操作。

深克隆示意图如下所示:
在这里插入图片描述
深克隆技术实现了原型对象和克隆对象的完全独立,对任意克隆对象的修改都不会给其他对象产生影响,是一种更为理想的克隆实现方式。

3、扩展

Java语言提供的Cloneable接口和Serializable接口的代码非常简单,它们都是空接口,这种空接口也称为标识接口。标识接口中没有任何方法的定义,其作用是告诉JRE这些接口的实现类是否具有某个功能,例如是否支持克隆、是否支持序列化等。

3、总结

原型模式作为一种快速创建大量相同或相似对象的方式,在软件开发中应用较为广泛,很多软件提供的复制(Ctrl+C)和粘贴(Ctrl+V)操作就是原型模式的典型应用。

3.1、优点

(1)当创建新的对象实例较为复杂时,使用原型模式可以简化对象的创建过程,通过复制一个已有实例可以提高新实例的创建效率。
(2)扩展性较好。由于在原型模式中提供了抽象原型类,在客户端可以针对抽象原型类进行编程,而将具体原型类写在配置文件中,增加或减少具体原型类对原有系统都没有任何影响。
(3)原型模式提供了简化的创建结构。工厂方法模式常常需要有一个与产品类等级结构相同的工厂等级结构,而原型模式就不需要这样。原型模式中产品的复制是通过封装在原型类中的克隆方法实现的,无须专门的工厂类来创建产品。
(4)可以使用深克隆的方式保存对象的状态。使用原型模式将对象复制一份并将其状态保存起来,以便在需要的时候使用,例如恢复到某一历史状态,可辅助实现撤销操作。

3.2、缺点

(1)需要为每一个类配备一个克隆方法,而且该克隆方法位于一个类的内部。当对已有的类进行改造时,需要修改源代码,违背了开闭原则。
(2)在实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多重的嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来可能会比较麻烦。

3.3、使用场景

(1)创建新对象成本较大(例如初始化需要占用较长的时间,占用太多的CPU资源或网络资源)。新的对象可以通过原型模式对已有对象进行复制来获得,如果是相似对象,则可以对其成员变量稍作修改。
(2)如果系统要保存对象的状态,而对象的状态变化很小,或者对象本身占用内存较少时,可以使用原型模式配合备忘录模式来实现。
(3)需要避免使用分层次的工厂类来创建分层次的对象,并且类的实例对象只有一个或很少的几个组合状态。通过复制原型对象得到新实例可能比使用构造函数创建一个新实例更加方便。

相关文章:

原型模式——对象的克隆

1、简介 1.1、概述 可以通过一个原型对象克隆出多个一模一样的对象,该模式被称为原型模式。 在使用原型模式时,需要首先创建一个原型对象,再通过复制这个原型对象来创建更多同类型的对象。 1.2、定义 原型模式(Prototype Patt…...

[SQL挖掘机] - 多表连接

介绍: 在 SQL 中,多表连接是指将多个表根据某些条件进行联接,以获取相关联的数据。这允许我们跨多个表进行查询,并且根据表之间的关系获取所需的结果。 作用: 当在多个表中存储相关数据时,使用多表连接可以将这些表组合起来以获…...

Day 14 C++ 对象的初始化和清理

目录 为什么要进行对象的初始化和清理 构造函数和析构函数 构造函数(Constructor) 构造函数语法 调用时机 构造函数的调用方式 括号法 显式法 隐式转换法 构造函数分类 分类方式 按参数分为有参构造和无参构造 按类型分为普通构造和拷贝构造…...

Delphi7最佳登录窗体设计

Delphi7我们这里用登录窗体来做演示。输入正确用户名和密码后,登录窗体释放,显示主窗体。 方法/步骤 1.打开Delphi7集成开发环境,在默认工程的Form1窗体放置一个Label1控件,拖动控件边界调整大小,并将Object Inspect…...

动脑学院Jetpack Compose学习笔记

最近b站学习了一下Compose相关内容,整理了相关笔记,仅供大家参考。 资源链接如下,象征性收取1个积分 https://download.csdn.net/download/juliantem/88125198...

Qt中线程的使用

Qt中线程的使用 在qt中线程的使用有两种方式,第一种就是创建一个类继承QObject类,之后使用moveToThread函数将线程添加到类中。另一种就是创建一个类继承QThread类,在类中实现run函数。 第一种方式: 1、首先创建一个自定义的类…...

基于YOLOv8开发构建蝴蝶目标检测识别系统

在前面的一篇博文中已经很详细地描述了如何基于YOLOv8开发构建自己的个性化目标检测模型,感兴趣的话可以看下: 《基于YOLOv8开发构建目标检测模型超详细教程【以焊缝质量检测数据场景为例】》 本文的主要目的就是基于YOLOv8来开发构建细粒度的蝴蝶目标…...

【已解决】电脑连上网线但无法上网

文章目录 案例情况解决方案必要的解决方法简要概括详细步骤1、打开控制面板2、打开更改适配器设置3、 找Internet协议版本44、修改配置 可能有用的解决方法 问题解决原理Internet 协议版本 4(TCP/IPv4)确保IP地址和DNS服务器设置为自动获取 案例情况 网…...

Linux 学习记录57(ARM篇)

Linux 学习记录57(ARM篇) 本文目录 Linux 学习记录57(ARM篇)一、外部中断1. 概念2. 流程图框 二、相关寄存器1. GIC CPU Interface (GICC)2. GIC distributor (GICD)3. EXTI registers 三、EXTI 寄存器1. 概述2. 内部框图3. 寄存器功能描述4. EXTI选择框图5. EXTI_EXTICR1 &…...

Doris注意事项,Doris部署在阿里云,写不进去数据

1.Doris官网 Doris官网https://doris.apache.org/ 2.根本原因 本地idea访问FE,FE会返回BE的地址,但是在服务器上通过ip addr查看,发现只有局域网IP,所以FE返回了局域网的IP,导致idea连接不上BE 3.解决办法 重写Ba…...

502 Bad GateWay报错的解决方法

什么是502 bad gateway 报错 简单来说 502 是报错类型代码 bad gateway 错误的网关。是Web服务器作为网关或代理服务器时收到无效的响应。 用我们的口语说就是运行网站的服务器暂时挂了(不响应)。 产生错误的原因 1.连接超时 我们向服务器发送请求 由于服务器当前链接太多&am…...

openpnp - ReferenceStripFeeder 改版零件

文章目录 openpnp - ReferenceStripFeeder 改版零件概述笔记整体效果散料飞达主体磁铁仓盖板飞达编带中间压条飞达编带两边压条装配体用的8mm编带模型END openpnp - ReferenceStripFeeder 改版零件 概述 官方推荐了ReferenceStripFeeder的模型smd_strip_feeders_mod_tray.zip…...

VoxPoser:使用大语言模型(GPT-4)来对机器人操作的可组合三维值图【论文解读】

这是最近斯坦福的李飞飞团队的一篇论文:VoxPoser: Composable 3D Value Maps for Robotic Manipulation with Language Models 主要是通过大语言模型LLM和视觉语言模型VLM结合,来对机器人做各种日常操作,我们可以先来看下实际效果:大语言模型…...

RISC-V公测平台发布 · 第一个WEB Server “Hello RISC-V world!”

RISC-V公测平台Web Server地址:http://175.8.161.253:8081 一、前言 Web Server是互联网应用的基础设施,无论是用户访问网站,还是后端服务提供商和开发者构建各种应用程序,Web Server都在其中扮演着至关重要的角色。 显而易见…...

Linux 发行版 CentOS 于 Ubuntu 软件的安装、卸载、查找

CentOS于Ubuntu 内核都是Linux,是一样的。 CentOS 软件格式 .rpm sudo yum [-y] [ install | remove | search ] 软件名称 install 安装 remove 移除 search 搜索 Ubuntu 软件格式 .deb sudo apt [-y] [ install | remove | search ] 软件名称 install 安装 remove…...

cmd相关操作命令

1.根据端口号查询对应进程的PID netstat -ano | findstr 端口号 例如:netstat -ano | findstr 9080;该端口所属进程的PID为6684 2.根据PID查询对应进程 tasklist | findstr PID 例如:tasklist | findstr 6684;该PID所属进程名为…...

使用EM算法完成聚类任务

EM算法(Expectation-Maximization Algorithm)是一种基于迭代优化的聚类算法,用于在无监督的情况下将数据集分成几个不同的组或簇。EM算法是一种迭代算法,包含两个主要步骤:期望步骤(E-step)和最…...

❤️创意网页:创意视觉效果粒子循环的网页动画

✨博主:命运之光 🌸专栏:Python星辰秘典 🐳专栏:web开发(简单好用又好看) ❤️专栏:Java经典程序设计 ☀️博主的其他文章:点击进入博主的主页 前言:欢迎踏入…...

【MTI 6.S081 Lab】thread

【MTI 6.S081 Lab】thread 前言调度Uthread: switching between threads (moderate)实验任务Hints解决方案thread_switchthread_create()thread_schedule() Using threads (moderate)实验任务解决方案 Barrier (moderate)实验任务解决方案 本实验前去看《操作系统导论》第29章基…...

AWS / VPC 云流量监控

由于安全性、数据现代化、增长、灵活性和成本等原因促使更多企业迁移到云,将数据存储在本地的组织正在使用云来存储其重要数据。亚马逊网络服务(AWS)仍然是最受追捧和需求的服务之一,而亚马逊虚拟私有云(VPC&#xff0…...

【OSG学习笔记】Day 18: 碰撞检测与物理交互

物理引擎(Physics Engine) 物理引擎 是一种通过计算机模拟物理规律(如力学、碰撞、重力、流体动力学等)的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互,广泛应用于 游戏开发、动画制作、虚…...

VB.net复制Ntag213卡写入UID

本示例使用的发卡器:https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解

【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下: 一、场景操作步骤 操作步…...

在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 …...

【网络安全产品大调研系列】2. 体验漏洞扫描

前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...

HTML 列表、表格、表单

1 列表标签 作用:布局内容排列整齐的区域 列表分类:无序列表、有序列表、定义列表。 例如: 1.1 无序列表 标签:ul 嵌套 li,ul是无序列表,li是列表条目。 注意事项: ul 标签里面只能包裹 li…...

质量体系的重要

质量体系是为确保产品、服务或过程质量满足规定要求,由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面: 🏛️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限,形成层级清晰的管理网络&#xf…...

剑指offer20_链表中环的入口节点

链表中环的入口节点 给定一个链表,若其中包含环,则输出环的入口节点。 若其中不包含环,则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...

基础测试工具使用经验

背景 vtune,perf, nsight system等基础测试工具,都是用过的,但是没有记录,都逐渐忘了。所以写这篇博客总结记录一下,只要以后发现新的用法,就记得来编辑补充一下 perf 比较基础的用法: 先改这…...

ffmpeg(四):滤镜命令

FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...