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

原型模式简介

概念

原型模式 (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。可以观察到两个对象是独立的(因为引用地址不同),它们的列表也是独立的副本,所以对列表的修改不会相互影响。需要注意的是当被克隆的类包含不可被序列化或反序列化的字段(如线程、文件句柄等),则需要采取额外措施来处理这些字段。

相关文章:

原型模式简介

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

SpringMVC(一)

1.SpringMVC简介 1.1 什么是MVC MVC是一种软件架构的思想&#xff0c;将软件按照模型、视图、控制器来划分 M:Model,模型层&#xff0c;指工程中的JavaBean,作用是处理数据 JavaBean分为两类&#xff1a; 一类称为实体类Bean:专门存储业务逻辑的&#xff0c;如Student、Us…...

树的基本概念和存储结构

一、树的基本概念 1、树的定义 树是n&#xff08;n>0&#xff09;个结点的有限集。当n 0时&#xff0c;称为空树。在任意一棵非空树中应满足&#xff1a; 1、有且仅有一个特定的称为根的结点。 2、当n>1时&#xff0c;其余节点可分为m&#xff08;m>0&#xff09…...

深圳企业制作宣传片群体定位的重要性

相信众多企业都拍了自己公司的宣传片&#xff0c;尤其是在互联网高度发达的今天&#xff0c;宣传片可以说成为了我们企业对外宣传最主要的方式。看着企业多样式的宣传片种类&#xff0c;我们该如何评价一部宣传片的好坏呢&#xff1f;要知道宣传片的好坏是一个相对主观的问题&a…...

2309亚当arsd的11.1版本

原文 arsd11.1 Minigui 调整主题 在11.0中略有修改Minigui的主题,但它落后于11.1的计划.这是个重大更改,但这些更改很小. 新主题稍微变浅了默认组件的背景色和默认字体,这两者都主要影响Linux,因为窗口上的大多数组件一般使用本地主题. 更改状态栏 现有的状态栏类允许添加…...

spring---第七篇

系列文章目录 文章目录 系列文章目录一、什么是bean的自动装配,有哪些方式?一、什么是bean的自动装配,有哪些方式? 开启自动装配,只需要在xml配置文件中定义“autowire”属性。 <bean id="cutomer" class="com.xxx.xxx.Customer" autowire="…...

编程要搞明白的东西(二)

文章目录 一、简介二、面向对象编程基础2.1 面向对象编程概述2.2 类和对象2.2.1 类的定义和特点2.2.2 对象的创建和使用 2.3 封装、继承与多态的关系2.3.1 封装的概念和优势2.3.2 继承的概念和作用2.3.3 多态的概念和实现方式 三、封装3.1 封装的定义和原则3.2 封装的实现方法3…...

检索与毒害 —— 对抗人工智能供应链攻击

作者&#xff1a;DAVE ERICKSON 在这篇文章中&#xff0c;了解人工智能大语言模型的供应链漏洞&#xff0c;以及如何利用搜索引擎的人工智能检索技术来对抗人工智能的错误信息和故意篡改。 虽然对于人工智能研究人员来说可能是新鲜事&#xff0c;但供应链攻击对于网络安全世界…...

Linux 禁止用户或 IP通过 SSH 登录

Linux 禁止用户或 IP通过 SSH 登录 限制用户 SSH 登录 1.只允许指定用户进行登录(白名单): 在 /etc/ssh/sshd_config 配置文件中设置 AllowUsers 选项,(配置完成需要重启 SSHD 服务)格式如下: AllowUsers aliyun test@192.168.1.1 # 允许 aliyun 和从 19…...

14.Redis 主从复制

Redis 主从复制 redis 主从复制配置 redis 主从复制启动 redis 主从复制断开 redis 主从复制主从复制构特点主从复制的拓扑结构一主一从⼀主多从树状主从 主从复制原理数据同步psync 运行流程全量复制流程部分复制流程实时复制 关于从节点何时晋升成主节点总结 redis 主从复制 …...

常见的图像格式介绍:RAW、RGB、YUV

常见的图像格式有RAW、RGB、YUV这三大类 1. RAW raw图像指的是sensor输出的原始数据&#xff0c;常见的有8位、10位、12位之分&#xff0c;分别表示一个像素点所占的字节数为8bit、10bit、12bit。 raw数据常见的有四种Bayer模式&#xff1a;GRBG、RGGB、BGGR&#xff08;下图…...

极简极速-Bitset (bitmap)实现考勤打卡场景

文章目录 1. redis命令行操作bitmap2. RedisTemplate操作bitmap3. Java中的Bitset 1. redis命令行操作bitmap 2. RedisTemplate操作bitmap bitmap的常见业务场景主要有日活统计&#xff08;类似的月考勤&#xff09;、点赞、BloomFilter等&#xff0c;以用户mj考勤统计为例&am…...

word如何插入图片?3种常用的方法

word作为一款常用的办公软件&#xff0c;不仅可以处理文本内容&#xff0c;还能够轻松地插入图片以丰富文档内容。插入图片可以使文档更具吸引力、可读性和信息传达能力。本文将为您介绍word如何插入图片的3种方法&#xff0c;帮助您在文档中灵活、高效地添加图像元素。 word插…...

Python/C API - 模組,型別,Tuple,例外和引用計數

Python/C API - 模組&#xff0c;型別&#xff0c;Tuple&#xff0c;例外和引用計數 前言Python/C API - Common Object StructuresPyObjectPyMethodDefPyGetSetDef Python/C API - Module ObjectsPyModuleDefPyModule_CreatePyModule_AddObjectPyModule_AddObjectRef Initiali…...

人工智能轨道交通行业周刊-第59期(2023.9.4-9.10)

本期关键词&#xff1a;无锡智慧地铁、无人车站、钢轨打磨、混元大模型、开源大模型 1 整理涉及公众号名单 1.1 行业类 RT轨道交通人民铁道世界轨道交通资讯网铁路信号技术交流北京铁路轨道交通网上榜铁路视点ITS World轨道交通联盟VSTR铁路与城市轨道交通RailMetro轨道世界…...

ASP.NET Core 中的 MVC架构

MVC 架构 MVC架构把 App 按照逻辑分成三层&#xff1a; Controllers&#xff0c;接收 http request&#xff0c;配合 model&#xff0c;通过http response 返回 view&#xff0c;尽量不做别的事Models, 负责业务逻辑&#xff0c;App 的状态&#xff0c;以及数据处理Views&…...

C# PSO 粒子群优化算法 遗传算法 随机算法 求解复杂方程的最大、最小值

复杂方程可以自己定义&#xff0c;以下是看别人的题目&#xff0c;然后自己来做 以下是计算结果 private void GetMinResult(out double resultX1, out double min){double x1, result;Random random1 new Random(DateTime.Now.Millisecond* DateTime.Now.Second);min 99999…...

网络协议从入门到底层原理学习(三)—— 路由

网络协议从入门到底层原理学习&#xff08;三&#xff09;—— 路由 1、简介 路由&#xff08;routing&#xff09;是指分组从源到目的地时&#xff0c;决定端到端路径的网络范围的进程 在不同网段之间转发数据&#xff0c;需要有路由器的支持 默认情况下&#xff0c;路由器…...

2023/9/6 -- C++/QT

一、输出流对象cout 1> 该对象是来自于ostream的类对象&#xff0c;功能上类似于printf函数 2> 该类对象本质上调用成员函数插入运算符重载函数 3> 输出数据时&#xff0c;无需使用格式控制符&#xff1a;%d、%c、%s。。。&#xff0c;直接输出即可 4> 换行使用…...

python爬虫,多线程与生产者消费者模式

使用队列完成生产者消费者模式使用类创建多线程提高爬虫速度 https://sc.chinaz.com/tupian/index.html https://sc.chinaz.com/tupian/index_2.html https://sc.chinaz.com/tupian/index_3.html from threading import Thread from queue import Queue import requests from b…...

每日一书㉗ | 刻意练习:为什么有些人努力一辈子还是平庸?

“本文来自「乐想屋」公众号&#xff0c;系列更新[每日一书]&#xff0c;每次5分钟&#xff0c;帮你把书读薄&#xff0c;把知识用活”先问你一个问题。你身边有没有这样的人&#xff1a;入行时间比你短&#xff0c;但能力已经甩你好几条街。他们好像没有特别刻苦&#xff0c;但…...

面试官问LinkedBlockingQueue和ArrayBlockingQueue区别?别只答有界无界了,这3个实战坑才是重点

面试官追问LinkedBlockingQueue与ArrayBlockingQueue&#xff1f;别只答基础区别&#xff0c;这3个实战陷阱才是关键 当面试官抛出"LinkedBlockingQueue和ArrayBlockingQueue有什么区别"这个问题时&#xff0c;80%的候选人会条件反射般回答"一个有界一个无界&qu…...

失传34年的南极DOS游戏LAN - LOK重见天日,背后藏着怎样的历史?

LAN - LOK&#xff1a;失传34年的南极DOS破坏游戏这是一次对历史进行重构&#xff08;或许还会进行现代化改造&#xff09;的尝试。AlphaPixel常处理遗留代码库&#xff0c;接触到80年代和90年代用各种方言和语言编写、存储在难处理容器和介质中的代码。因保密协议&#xff0c;…...

Arduino新手避坑指南:用DHT11温湿度传感器做个简易气象站(附完整代码)

Arduino实战&#xff1a;DHT11温湿度传感器避坑指南与气象站搭建第一次接触Arduino和传感器的新手们&#xff0c;是否经常遇到数据读取失败、数值异常或者硬件连接不稳定的问题&#xff1f;本文将带你从零开始&#xff0c;避开那些教科书上不会告诉你的坑&#xff0c;完成一个稳…...

一文讲透|高效论文写作全流程AI论文工具推荐(2026 最新)

论文写作全流程可拆解为文献调研→选题/开题→大纲/初稿→文献综述→降重/去AI味→润色/格式→查重/投稿七大环节&#xff0c;以下工具按环节精准匹配&#xff0c;兼顾中文适配、降重能力、去AI痕迹、学术合规四大核心需求&#xff0c;覆盖免费/付费、通用/垂直场景。2026年&am…...

从PointNet到Transformer:聊聊‘参数共享’这个省内存又提性能的炼丹技巧

从PointNet到Transformer&#xff1a;参数共享如何重塑深度学习效率 在深度学习模型日益复杂的今天&#xff0c;算法工程师们不断面临一个核心矛盾&#xff1a;如何在保持模型性能的同时&#xff0c;有效控制参数规模&#xff1f;当我们处理点云、序列或图结构这类不规则数据时…...

从一次PR被拒说起:我是如何用Git Upstream优雅同步Gitee Fork仓库的

从一次PR被拒说起&#xff1a;我是如何用Git Upstream优雅同步Gitee Fork仓库的 那天下午&#xff0c;当我满怀期待地打开Gitee通知&#xff0c;看到的却是项目维护者冰冷的回复&#xff1a;"PR存在大量冲突&#xff0c;请先同步最新代码"。作为一个刚接触开源贡献的…...

如何快速搭建个人小说图书馆:番茄小说下载器完整实战指南

如何快速搭建个人小说图书馆&#xff1a;番茄小说下载器完整实战指南 【免费下载链接】Tomato-Novel-Downloader 番茄小说下载器不精简版 项目地址: https://gitcode.com/gh_mirrors/to/Tomato-Novel-Downloader 你是否曾经遇到过这样的问题&#xff1a;想离线阅读喜欢的…...

大语言模型在嵌入式系统开发中的应用与挑战

1. 嵌入式系统开发与大语言模型的碰撞 在智能家居、工业自动化和物联网设备蓬勃发展的今天&#xff0c;嵌入式系统作为连接数字世界与物理世界的桥梁&#xff0c;其开发复杂度正呈指数级增长。传统嵌入式开发要求工程师同时具备三大核心能力&#xff1a;理解电子元件特性与电路…...

JMeter实战:从接口测试到性能基线的全链路压测指南

1. 这不是“点点点就能跑通”的测试&#xff0c;而是用JMeter撬动系统稳定性的杠杆很多人第一次打开JMeter&#xff0c;以为它就是个“高级版Postman”&#xff1a;填URL、选方法、点执行&#xff0c;看到Response里有JSON就松一口气——“接口通了&#xff0c;测试完了”。我带…...