原型模式简介

概念

原型模式 (Prototype Pattern)是一种创建型设计模式,它允许通过复制现有对象来创建新对象,而无需依赖于昂贵的实例化过程。该模式基于原型实例生成新的对象,并且可以根据需要进行修改和定制。

特点

  1. 通过克隆已有对象来创建新对象,避免了重复的初始化过程。
  2. 可以动态地添加或删除已有的原型。
  3. 提供了一种简单、灵活和高效的方式来创建多个相似对象。

优点

  1. 减少了重复代码,提高了代码复用性。
  2. 避免了耗时的实例化操作,提升性能。
  3. 简化了对象创建过程,使得系统更加灵活。

缺点

  1. 需要为每个可变属性添加适当的克隆方法,并在每次修改时更新这些方法。
  2. 如果存在循环引用,则需要小心处理克隆过程中可能出现的无限循环问题。

适用场景

  1. 当一个系统需要独立于其具体类型创建多个相似但不同配置项或版本时,可以使用原型模式。
  2. 当一个系统需要动态地加载和配置类实例时,可以使用原型模式。

实现方式

浅拷贝:

直接对目标对象进行浅拷贝,复制基本数据类型的值和引用类型的地址。

实现原理

  1. 创建一个新对象,并将原始对象中所有的字段值复制给新对象。
  2. 对于基本数据类型,直接进行值复制。
  3. 对于引用类型,只复制其地址而不创建新的实例。

实现代码:

import java.util.ArrayList;
import java.util.List;class Prototype implements Cloneable {private int id;private String name;private List<String> list;// 构造函数public Prototype(int id, String name, List<String> list) {this.id = id;this.name = name;this.list = list;}// Getter 和 Setterpublic void setList(List<String> list) {this.list = list;}public List<String> getList(){return this.list;}@Overridepublic Prototype clone() throws CloneNotSupportedException {return (Prototype) super.clone();}
}public class Main {public static void main(String[] args) {List<String> originalList = new ArrayList<>();originalList.add("item1");originalList.add("item2");Prototype originalObject = new Prototype(1, "Original", originalList);try {Prototype clonedObject = originalObject.clone();System.out.println(originalObject == clonedObject); // 输出 falseSystem.out.println(originalObject.getList() == clonedObject.getList()); // 输出 true} catch (CloneNotSupportedException e) {e.printStackTrace();}}
}

在上述示例中,Prototype 类实现了 Cloneable 接口,并重写了 clone() 方法。通过调用 super.clone() 进行浅拷贝操作,返回一个新的克隆对象。

接下来我们创建了一个原始对象 originalObject,并进行浅拷贝得到克隆对象 clonedObject。可以观察到两个对象不相等(因为引用地址不同),但是它们共享相同的引用类型字段(即列表),所以对列表的修改会同时影响两个对象。

存在问题
由于浅拷贝仅仅是对引用进行了简单地复制操作,并没有创建全新独立的副本,因此可能会导致以下问题:

  1. 如果被复制的引用类型发生改变,那么克隆后得到的副本也会受到影响。
  2. 在多层嵌套结构中,如果某个属性发生改变,则克隆后得到副本中对应属性也会改变。

深拷贝:

对目标对象进行深拷贝,复制基本数据类型的值和引用类型的整个对象。

实现原理

  1. 创建一个新对象,并将原始对象中所有字段值复制给新对象。
  2. 对于基本数据类型,直接进行值复制。
  3. 对于引用类型,在克隆过程中创建该引用类型的全新副本,并将副本赋值给新对象。

实现代码

import java.util.ArrayList;
import java.util.List;class Prototype implements Cloneable {private int id;private String name;private List<String> list;// 构造函数public Prototype(int id, String name, List<String> list) {this.id = id;this.name = name;this.list = list;}// Getter 和 Setterpublic List<String> getList() {return list;}public void setList(List<String> list) {this.list = list;}@Overridepublic Prototype clone() throws CloneNotSupportedException {Prototype clonedObject = (Prototype) super.clone();clonedObject.list = new ArrayList<>(this.list); // 创建全新副本return clonedObject;}
}public class Main {public static void main(String[] args) {List<String> originalList = new ArrayList<>();originalList.add("item1");originalList.add("item2");Prototype originalObject = new Prototype(1, "Original", originalList);try {Prototype clonedObject = originalObject.clone();System.out.println(originalObject == clonedObject); // 输出 falseSystem.out.println(originalObject.getList() == clonedObject.getList()); // 输出 false} catch (CloneNotSupportedException e) {e.printStackTrace();}}
}

在上述示例中,Prototype 类实现了 Cloneable 接口,并重写了 clone() 方法。通过调用 super.clone() 进行浅拷贝操作,然后对引用类型字段进行深层次克隆。

接下来我们创建了一个原始对象 originalObject,并进行深拷贝得到克隆对象 clonedObject。可以观察到两个对象完全独立(因为引用地址不同),它们的列表也是独立的副本,所以对列表的修改不会相互影响。

深拷贝虽然解决了浅拷贝可能带来的问题,但也存在以下问题:

  1. 如果被复制的引用类型内部还包含其他引用类型,则需要递归地进行深层次克隆操作。
  2. 某些类可能无法被序列化或反序列化而导致无法使用标准方法实现深拷贝。

原型注册表

使用原型管理器来存储和获取原型实例,可以通过名称或标识符查找并克隆相应的原型。

实现原理

  1. 创建一个原型注册表类(通常为单例),用于存储和管理多个不同类型的原型对象。
  2. 在注册表中添加方法,用于向注册表中添加、删除或获取指定名称的原型对象。
  3. 当客户端需要创建新对象时,可以通过调用注册表中相应名称的方法来获取对应的克隆副本。

实现代码

import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
import java.util.Map;class Prototype implements Cloneable {private int id;private String name;private List<String> list;// 构造函数public Prototype(int id, String name, List<String> list) {this.id = id;this.name = name;this.list = list;}// Getter 和 Setterpublic void setName(String name){this.name = name;}public String getName() {return this.name;}public List<String> getList() {return list;}public void setList(List<String> list) {this.list = list;}@Overridepublic Prototype clone() throws CloneNotSupportedException {Prototype clonedObject = (Prototype) super.clone();clonedObject.list = new ArrayList<>(this.list); // 创建全新副本return clonedObject;}
}class PrototypeRegistry {private static PrototypeRegistry instance;private Map<String, Prototype> prototypes;private PrototypeRegistry() {prototypes = new HashMap<>();}public static synchronized PrototypeRegistry getInstance() {if (instance == null) {instance = new PrototypeRegistry();}return instance;}public void addPrototype(String name, Prototype prototype) {prototypes.put(name, prototype);}public void removePrototype(String name) {prototypes.remove(name);}public Prototype getPrototype(String name) throws CloneNotSupportedException {Prototype prototype = prototypes.get(name);if (prototype != null) {return prototype.clone();}return null;}
}public class Main {public static void main(String[] args) {
// 创建原始对象并添加到注册表List<String> originalList = new ArrayList<>();originalList.add("item1");originalList.add("item2");Prototype originalObject = new Prototype(1, "Original", originalList);PrototypeRegistry registry = PrototypeRegistry.getInstance();registry.addPrototype("object1", originalObject);// 从注册表获取副本并进行修改try {Prototype clonedObject = registry.getPrototype("object1");if (clonedObject != null) {clonedObject.setName("Cloned");System.out.println(originalObject.getName()); // 输出 "Original"System.out.println(clonedObject.getName()); // 输出 "Cloned"System.out.println(originalObject.getList() == clonedObject.getList()); // 输出 false}} catch (CloneNotSupportedException e) {e.printStackTrace();}}
}

在深拷贝实现代码的基础上,我们增加了PrototypeRegistry 类实现了原型注册表的功能。通过使用单例模式保证只有一个注册表实例,并使用 HashMap 存储原型对象。然后创建了一个原始对象 originalObject 并将其添加到注册表中。然后通过调用 getPrototype() 方法从注册表中获取副本,并对副本进行修改。可以观察到两个对象是独立的,对一个对象的修改不会影响到另一个对象。

序列化与反序列化

将对象序列化为字节流,然后再反序列化为新对象。

实现原理

  1. 被克隆的对象必须实现 Serializable 接口。
  2. 序列化:使用 ObjectOutputStream 将对象写入输出流,并将其转换为字节数组。
  3. 反序列化:使用 ObjectInputStream 从输入流中读取字节数组,并将其转换回原始对象。

实现代码

import java.io.*;
import java.util.ArrayList;
import java.util.List;class Prototype implements Serializable {private int id;private String name;private List<String> list;// 构造函数public Prototype(int id, String name, List<String> list) {this.id = id;this.name = name;this.list = list;}// Getter 和 Setterpublic List<String> getList() {return list;}public Prototype deepClone() {try {ByteArrayOutputStream baos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(baos);oos.writeObject(this);ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bais);return (Prototype) ois.readObject();} catch (IOException | ClassNotFoundException e) {e.printStackTrace();}return null;}
}public class Main {public static void main(String[] args) {List<String> originalList = new ArrayList<>();originalList.add("item1");originalList.add("item2");Prototype originalObject = new Prototype(1, "Original", originalList);Prototype clonedObject = originalObject.deepClone();System.out.println(originalObject == clonedObject); // 输出 falseSystem.out.println(originalObject.getList() == clonedObject.getList()); // 输出 false}
}

在上述示例中,Prototype 类实现了 Serializable 接口,并提供了一个 deepClone() 方法来进行深拷贝。在该方法中,首先将对象写入输出流并转换为字节数组,然后再从输入流中读取字节数组并转换回对象。然后我们创建了一个原始对象 originalObject 并通过调用 deepClone() 方法进行深拷贝得到副本对象 clonedObject。可以观察到两个对象是独立的(因为引用地址不同),它们的列表也是独立的副本,所以对列表的修改不会相互影响。需要注意的是当被克隆的类包含不可被序列化或反序列化的字段(如线程、文件句柄等),则需要采取额外措施来处理这些字段。

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

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

相关文章

当你的公司突然开始大量的裁员,被留下的你,真的准备好面对以后了吗?

留下来的&#xff0c;也是迷茫的 最近公司突然开始大量裁员&#xff0c;裁了一多半&#xff0c;作为唯一留下的APP 端开发人员&#xff0c;也开始陷入了焦虑&#xff0c;开始了思考&#xff0c;未来究竟何去何从&#xff0c;是否再去转到原生&#xff0c;从事原生的开发工作&a…

C语言入门Day_19 初识函数

目录 1.函数的定义 2.函数的调用 3.易错点 4.思维导图 前言&#xff1a; printf()我们已经很熟悉了&#xff0c;它有一个特定的功能&#xff0c;就是在屏幕上输出一行文字。之前的课程我们都称呼printf()为一个功能&#xff0c;实际上ta在编程中有个特定的名字——函数。 …

测试-----selenuim webDriver

文章目录 1.页面导航2.元素定位3. 浏览器操作4.获取元素信息5. 鼠标的操作6. 键盘操作7. 元素等待8.下拉框9.弹出框10.滚动条11.frame处理12.验证码处理&#xff08;cookie&#xff09; 1.页面导航 首先是导入对应的包 :from selenium import webdriver然后实例化:driver web…

MySQL——库和表的增删改

2023.9.12 本章学习DDL&#xff08;数据定义语言&#xff09;语言&#xff0c;相关笔记&#xff1a; #DDL /*数据定义语言库和表的管理一、库的管理 创建、修改、删除 二、表的管理 创建、修改、删除创建&#xff1a; create 修改&#xff1a; alter 删除&#xff1a; drop*/#…

标准C++day5——静态成员和单例模式(饿汉、懒汉)

一、静态成员 什么是静态成员&#xff1a; 被static修饰的成员变量和成员函数就叫静态成员 普通成员的特点&#xff1a; 成员变量&#xff1a;每个类对象中都有一份属于自己的成员变量&#xff0c;相互之间没有关联、独立的 成员函数&#xff1a;隐藏着一个this指针&#xff0c…

网络安全宣传周|探索AI数字人的魅力和价值所在

9月11日至9月17日是国家网络安全宣传周&#xff0c;在福州举办的安全博览会上有着多种人工智能模型产品亮相现场&#xff0c;吸引着众多参观者的目光&#xff0c;尤其是AI数字人面对不同的问题、不同的场景都可以进行实时响应&#xff0c;不同于冷冰冰的传统智能客服的对话场景…

2023-09-05力扣每日一题

链接&#xff1a; 2605. 从两个数字数组里生成最小数字 题意&#xff1a; 两个数组都只包含1-9的数字&#xff0c;求一个最小数&#xff0c;两个数组内都要有它的其中一位 解&#xff1a; 要么是个位数要么是十位数&#xff0c;存一下数量和两边的最小数即可 实际代码&am…

前端面试合集(二)

前端面试题合集 1.懒加载的原理及实现了解吗2.如何理解JS异步3.阐述一下 JS 的事件循环4.JS 中的计时器能做到精确计时吗&#xff1f;为什么&#xff1f; 1.懒加载的原理及实现了解吗 原理&#xff1a;当图片没有到达可视范围内时&#xff0c;图片不加载&#xff0c;当图片一旦…

Mobileye CEO来华:只有能控制住成本的公司,才能活下来

‍作者|德新 编辑|王博 上午9点近一刻&#xff0c;Mobileye CEO Amnon Shuashua步入酒店的会议室。由于Amnon本人是以色列希伯来大学的计算机科学教授&#xff0c;大部分人更习惯称他为「教授」。 时近以色列的新年&#xff0c;这趟教授的中国之行安排十分紧凑。 他率领了一…

使用go语言实现自建区块链系统的一些环境的一些总结

1.go版本 可以使用gvm安装&#xff0c;但gvm需要科学上网才能下载&#xff0c;而且安装脚本内部有的也需要科学上网&#xff0c;如果其他的最好还是安装gvm 因此这里直接安装go go官网选择go1.17.1.linux-amd64.tar.gz ### 1.解压 tar -xvf go1.17.1.linux-amd64.tar.gz ### …

遥遥领先的内存函数

目录 ​编辑 函数介绍 1.1 strlen 1.2 strcpy 1.3 strcmp 1.4 strcat 1.5 strstr 2.1 memcpy 2.2 memmove 2.3 memcmp 函数实现 1.1 strlen 1.2 strcpy 1.3 strcmp 1.4 strcat 1.5 strstr 2.1 memcpy 2.3 memcmp 函数介绍 1.1 strlen size_t strlen ( const char *…

iOS CoreMotion获取传感器数据

CoreMotion是iOS系统目前用于处理加速度计&#xff0c;陀螺仪&#xff0c;计步器和环境相关事件。 Core Motion的报告来自iOS设备的板载硬件的运动和环境相关数据&#xff0c;包括加速度计和陀螺仪&#xff0c;以及计步器&#xff0c;磁力计和气压计。 为了以特定的时间间隔接…

SpringBoot整合Redis,基于Jedis实现redis各种操作

前言&#xff08;三步教你学会redis&#xff0c;主打一个实用&#xff09; springboot整合redis步骤&#xff0c;并基于jedis对redis数据库进行相关操作&#xff0c;最后分享非常好用、功能非常全的redis工具类。 第一步&#xff1a;导入maven依赖 <!-- springboot整合re…

小程序代码管理

“微信开发者工具”点击版本管理&#xff0c;然后点击代码管理会打开代码管理网页。 选择对应的项目组。 进来后点击创建项目。 输入git名称&#xff0c;然后选择命名空间&#xff0c;最后创建即可。 在刚才的“微信开发者工具”选择设置&#xff0c;然后添加远程。 输入名称&…

朋友圈大佬都去读研了,这份备考书单我码住了

作者简介&#xff1a; 辭七七&#xff0c;目前大二&#xff0c;正在学习C/C&#xff0c;Java&#xff0c;Python等 作者主页&#xff1a; 七七的个人主页 文章收录专栏&#xff1a; 七七的闲谈 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01;&#x1f496;&#x1f…

pycharm安装jupyter,用德古拉主题,但是输入行全白了,看不清,怎么办?

问题描述 今天换了以下pycharm主题&#xff0c;但是jupyter界面输入代码行太白了&#xff0c;白到看不清楚这行的字&#xff0c;更不知道写的是什么&#xff0c;写到哪了&#xff0c;这还是挺烦人的&#xff0c;其他都挺正常的。 问题分析 目前来看有两个原因&#xff1a; 1、…

【新版vscode配置自动补全括号】

vscode新版配置自动补全括号方法 新版配置方法(Pylance) ①设置 -> 扩展 -> Pylance -> 将 python.analysis.completeFunctionParens 下方的选项打勾 ②直接在settings.json中添加"python.analysis.completeFunctionParens": true

【Apollo】自动驾驶技术的介绍

阿波罗是百度发布的名为“Apollo&#xff08;阿波罗&#xff09;”的向汽车行业及自动驾驶领域的合作伙伴提供的软件平台。 帮助汽车行业及自动驾驶领域的合作伙伴结合车辆和硬件系统&#xff0c;快速搭建一套属于自己的自动驾驶系统。 百度开放此项计划旨在建立一个以合作为中…

vue3 - setup之defineEmits

基础形式 子组件 const emits defineEmits([name])//触发emits事件 const eventButton () > {emits(name) }父组件 <script> const handleName () >{ } </script> <template><ComponentName name"handleName" /> </template>…

如何评估ChatGPT的语言多样性和方言识别能力?

评估ChatGPT的语言多样性和方言识别能力是重要的&#xff0c;特别是在处理全球性用户群体和多语种社区的情况下。这需要综合考虑ChatGPT的能力来理解、生成和适应不同语言和方言。在这篇回答中&#xff0c;我将详细讨论如何评估ChatGPT的语言多样性和方言识别能力&#xff0c;并…