原型模式(创建型)

一、前言

        原型模式是一种创建型设计模式,它允许在运行时通过克隆现有对象来创建新对象,而不是通过常规的构造函数创建。在原型模式中,一个原型对象可以克隆自身来创建新的对象,这个过程可以通过深度克隆或浅克隆来实现。简单说原型模式其实就是从一个对象再创建另外一个可定制的对象,而且不需知道任何创建的细节。

        原型模式包含三个角色:

        ①Prototype(抽象原型类):定义用于克隆自身的接口,通常是一个抽象类或接口,其中声明了一个克隆方法 clone(),用于创建一个新的对象,具体的克隆操作由子类实现。

        ②ConcretePrototype(具体原型类):实现 Prototype 接口,实现 clone() 方法,完成对象的克隆操作。每个具体原型类都有自己的一些属性和方法,不同的具体原型类之间具有不同的实现方式。

        ③Client(客户类):使用原型模式创建新的对象,在原型模式中,客户端通过克隆接口来创建新的对象,而不是通过实例化的方式。客户端需要获取一个原型对象,并通过克隆方法来创建新的对象,从而避免了创建新对象的开销。

        原型模式的克隆分为浅拷贝和深拷贝两种。

二、浅拷贝

        创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。

        这里抽象原型类不用自己创建,都是Object,里面有clone()方法:

        具体原型类实现Cloneable,里面重写clone方法,直接super调用父类的clone方法即可:

import java.util.List;/*** @Author dengyifan* @create 2023/11/10 10:19* @description*/
public class UserPrototype implements Cloneable{private String name;private Integer age;private List<String> messages;public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public List<String> getMessages() {return messages;}public void setMessages(List<String> messages) {this.messages = messages;}@Overrideprotected UserPrototype clone() throws CloneNotSupportedException {return (UserPrototype) super.clone();}
}

        客户类:

import java.util.ArrayList;
import java.util.List;/*** @Author dengyifan* @create 2023/11/10 10:22* @description*/
public class PrototypeClient {public static void main(String[] args) throws CloneNotSupportedException {UserPrototype userPrototype1 = new UserPrototype();userPrototype1.setName("tom");userPrototype1.setAge(18);List<String> messages1 = new ArrayList<>();messages1.add("test");userPrototype1.setMessages(messages1);UserPrototype userPrototype2 = userPrototype1.clone();System.err.println(String.format("两个对象是否一致:%s", userPrototype1 == userPrototype2));System.err.println(String.format("两个对象的name属性是否一致:%s", userPrototype1.getName() == userPrototype2.getName()));System.err.println(String.format("两个对象的messages属性是否一致:%s", userPrototype1.getMessages() == userPrototype2.getMessages()));System.err.println(userPrototype1.getMessages());System.err.println(userPrototype2.getMessages());messages1.clear();messages1.add("test1");System.err.println(userPrototype1.getMessages());System.err.println(userPrototype2.getMessages());}
}

        运行结果:

        通过结果可以看到,通过克隆的对象与原型对象的引用不一致,说明再内存中存在两个不同的对象,一个是原型对象,一个是克隆生成的对象。而这里属性都显示是一致的,结果都是true,说明两个对象的成员对象是同一个,也就是对象本身是复制了,但是其成员的对象再内存中没有复制。并且我们修改原型的messages属性,克隆对象的messages也跟着改变,说明属性确实是同一个对象的引用。

三、深拷贝

        深拷贝中除了原型对象与克隆生成的对象被复制以外,里面的属性也需要被复制,即地址都不一样。这里深拷贝实现了Serializable,即进行了序列化。

import java.io.*;
import java.util.List;/*** @Author dengyifan* @create 2023/11/10 15:43* @description*/
public class UserPrototypeDeep implements Serializable {private String name;private Integer age;private List<String> messages;public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public List<String> getMessages() {return messages;}public void setMessages(List<String> messages) {this.messages = messages;}protected UserPrototypeDeep deepClone() throws Exception{ByteArrayOutputStream bao=new ByteArrayOutputStream();ObjectOutputStream oos=new ObjectOutputStream(bao);oos.writeObject(this);//将对象从流中取出ByteArrayInputStream bis=new ByteArrayInputStream(bao.toByteArray());ObjectInputStream ois=new ObjectInputStream(bis);return (UserPrototypeDeep) ois.readObject();}
}
import java.util.ArrayList;
import java.util.List;/*** @Author dengyifan* @create 2023/11/10 15:46* @description*/
public class PrototypeDeepClient {public static void main(String[] args) throws Exception {UserPrototypeDeep userPrototypeDeep1 = new UserPrototypeDeep();userPrototypeDeep1.setAge(19);userPrototypeDeep1.setName("tom");List<String> messages1 = new ArrayList<>();messages1.add("test");userPrototypeDeep1.setMessages(messages1);UserPrototypeDeep userPrototypeDeep2 = userPrototypeDeep1.deepClone();System.err.println(String.format("两个对象是否一致:%s", userPrototypeDeep1 == userPrototypeDeep2));System.err.println(String.format("两个对象的name属性是否一致:%s", userPrototypeDeep1.getName() == userPrototypeDeep2.getName()));System.err.println(String.format("两个对象的messages属性是否一致:%s", userPrototypeDeep1.getMessages() == userPrototypeDeep2.getMessages()));System.err.println(userPrototypeDeep1.getMessages());System.err.println(userPrototypeDeep2.getMessages());messages1.clear();messages1.add("test1");System.err.println(userPrototypeDeep1.getMessages());System.err.println(userPrototypeDeep2.getMessages());}
}

        运行结果:

        可以看出除了对象本身不一致以外,里面的属性也不一致,当修改原型对象里面引用对象的属性时,克隆对象的属性不会发生变化,即也证明属性不是同一个对象。

        对于原型模式的应用场景,主要有以下几点:

1. 当对象的创建过程非常复杂或耗费大量资源时,可以使用原型模式来复制一个现有对象,从而避免重复创建对象。

2. 当需要创建多个相似的对象时,可以使用原型模式来减少重复代码,提高代码的重用性。

3. 当创建对象的过程需要大量的数据准备或配置时,可以使用原型模式来复制一个已经配置好的对象,从而避免重复的数据准备或配置过程。

4. 当需要动态生成对象的子类时,可以使用原型模式来复制一个已有的对象,并对其进行修改,从而避免重新编写子类的代码。

5. 当需要保护对象的状态时,可以使用原型模式来创建对象的副本,并将副本交给其他对象使用,从而避免原始对象的状态被修改。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【PyQt】(自制类)处理鼠标点击逻辑

写了个自认为还算不错的类&#xff0c;用于简化mousePressEvent、mouseMoveEvent和mouseReleaseEvent中的鼠标信息。 功能有以下几点&#xff1a; 鼠标当前状态&#xff0c;包括鼠标左/中/右键和单击/双击/抬起鼠标防抖(仅超出一定程度时才判断鼠标发生了移动)&#xff0c;灵…

vue做的一个一点就转的转盘(音乐磁盘),点击停止时会在几秒内缓慢停止,再次点击按钮可以再次旋转,

先看效果&#xff1a; 代码&#xff1a;主要部分我会红线画出来 css:部分&#xff1a; 源码&#xff1a; vue部分&#xff1a; <template><div class"song-lyric"><div><div class"type"><div class"right">&l…

SpringBoot_01

Spring https://spring.io/ SpringBoot可以帮助我们非常快速的构建应用程序、简化开发、提高效率。 SpringBootWeb入门 需求&#xff1a;使用SpringBoot开发一个web应用&#xff0c;浏览器发起请求/hello后&#xff0c;给浏览器返回字符串"Hello World~~~"。 步骤…

srs webrtc推拉流环境搭建

官方代码https://github.com/ossrs/srs 拉取代码&#xff1a; git clone https://github.com/ossrs/srs.gitcd ./configure make ./objs/srs -c conf/rtc.confconf/rtc.conf中&#xff0c;当推拉流浏览器在本地时&#xff0c;如果srs也在本地&#xff0c;那么可以使用官网默认…

Azure 机器学习 - 机器学习中的企业安全和治理

目录 限制对资源和操作的访问网络安全性和隔离数据加密数据渗透防护漏洞扫描审核和管理合规性 在本文中&#xff0c;你将了解可用于 Azure 机器学习的安全和治理功能。 如果管理员、DevOps 和 MLOps 想要创建符合公司策略的安全配置&#xff0c;那么这些功能对其十分有用。 通过…

数据库安全:Hadoop 未授权访问-命令执行漏洞.

数据库安全&#xff1a;Hadoop 未授权访问-命令执行漏洞. Hadoop 未授权访问主要是因为 Hadoop YARN 资源管理系统配置不当&#xff0c;导致可以未经授权进行访问&#xff0c;从而被攻击者恶意利用。攻击者无需认证即可通过 RESTAPI 部署任务来执行任意指令&#xff0c;最终完…

KVM虚拟机迁移原理与实践

虚拟机迁移 迁移(migration)包括系统整体的迁移和某个工作负载的迁移&#xff0c;系统整体迁移是将系统上的所有软件&#xff0c;包括操作系统&#xff0c;完全复制到另一台物理硬件机器上&#xff0c;而工作负载迁移仅仅迁移特定的工作负载。 虚拟化技术的出现&#xff0c;丰…

upload 文件自动上传写法,前后端 下载流文件流

<el-uploadv-model:file-list"fileList":action"app.api/student/student/import":headers"{// Content-Type: multipart/form-data;boundary----split-boundary, 此处切记不要加&#xff0c;否则会造成后端报错 Required request part file is…

CSS 移动端 1px(线条/边框) 不同机型上显示粗细不同,解决办法

由于不同的手机有不同的像素密度导致的。如果移动显示屏的分辨率始终是普通屏幕的2倍&#xff0c;1px的边框在devicePixelRatio2的移动显示屏下会显示成2px&#xff0c;所以在高清瓶下看着1px总是感觉变胖了 <!DOCTYPE html> <html lang"en"> <head&g…

19.删除链表的倒数第N个结点(LeetCode)

想法一 先用tail指针找尾&#xff0c;计算出节点个数&#xff0c;再根据倒数第N个指定删除 想法二 根据进阶的要求&#xff0c;只能遍历一遍链表&#xff0c;那刚刚想法一就做不到 首先&#xff0c;我们要在一遍内找到倒数第N个节点&#xff0c;所以我们设置slow和fast两个指…

优化编辑距离以测量文本相似度

一、说明 编辑距离是一种文本相似度度量&#xff0c;用于测量 2 个单词之间的距离。它有许多方面应用&#xff0c;如文本自动完成和自动更正。 对于这两种用例中的任何一种&#xff0c;系统都会将用户输入的单词与字典中的单词进行比较&#xff0c;以找到最接近的匹配项&#x…

Codeforces Round 908 (Div. 2)题解

目录 A. Secret Sport 题目分析: B. Two Out of Three 题目分析: C. Anonymous Informant 题目分析: A. Secret Sport 题目分析: A,B一共打n场比赛&#xff0c;输入一个字符串由A和‘B’组成代表A赢或者B赢&#xff08;无平局&#xff09;&#xff0c;因为题目说明这个人…