全网最简单的Java设计模式【九】原型模式深入解析

如果觉得本文能够帮到您,请关注🌟、点赞👍、收藏📚,让这份美好延续下去!

一、引言

在 Java 软件开发中,设计模式起着至关重要的作用,它们为解决各种常见的软件设计问题提供了经过验证的方案。其中,原型模式是一种创建型设计模式,它允许通过复制现有对象来创建新对象,而无需了解对象的具体创建过程。这种模式在很多场景下都非常有用,尤其是当对象的创建过程较为复杂或者创建成本较高时。本文将深入探讨 Java 设计模式中的原型模式,包括其定义、实现方式、优缺点以及应用场景,并通过具体的代码示例进行详细说明。

二、原型模式的定义

原型模式(Prototype Pattern)是一种创建对象的方式,通过复制一个已经存在的实例来创建新的实例,而不是通过传统的构造函数来创建。这样可以避免一些复杂的对象初始化过程,提高对象创建的效率。在原型模式中,被复制的对象称为原型对象,新创建的对象称为克隆对象。

三、原型模式的实现方式

在 Java 中,原型模式可以通过两种方式实现:浅克隆(Shallow Clone)和深克隆(Deep Clone)。

(一)浅克隆实现

1. 假设我们有一个包含 基本数据类型引用类型的类

package com.jsglxx.design.prototype;import java.util.List;class Person implements Cloneable {private int age;private List<String> hobbies;public Person(int age, List<String> hobbies) {this.age = age;this.hobbies = hobbies;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public List<String> getHobbies() {return hobbies;}public void setHobbies(List<String> hobbies) {this.hobbies = hobbies;}@Overridepublic Object clone() throws CloneNotSupportedException {return super.clone();}
}

2. 使用示例:

package com.jsglxx.design.prototype;import java.util.ArrayList;
import java.util.List;public class ShallowCloneExample {public static void main(String[] args) {try {List<String> hobbies = new ArrayList<>();hobbies.add("Reading");Person original = new Person(30, hobbies);Person clone = (Person) original.clone();System.out.println("Original: Age = " + original.getAge() + ", Hobbies = " + original.getHobbies());System.out.println("Clone: Age = " + clone.getAge() + ", Hobbies = " + clone.getHobbies());// 修改原始对象的引用类型字段内容original.getHobbies().add("Drawing");original.setAge(35);System.out.println("After modification:");System.out.println("Original: Age = " + original.getAge() + ", Hobbies = " + original.getHobbies());System.out.println("Clone: Age = " + clone.getAge() + ", Hobbies = " + clone.getHobbies());} catch (CloneNotSupportedException e) {e.printStackTrace();}}
}

3. 输出结果:

Original: Age = 30, Hobbies = [Reading]
Clone: Age = 30, Hobbies = [Reading]
After modification:
Original: Age = 35, Hobbies = [Reading, Drawing]
Clone: Age = 30, Hobbies = [Reading, Drawing]

在这个浅克隆示例中:

  • 首先创建了一个原始对象 original,其中包含一个基本数据类型 age 和一个引用类型 hobbies 列表。当通过 super.clone() 进行浅克隆创建 clone 对象时,基本数据类型 age 被按值复制,所以原始对象和克隆对象的 age 是独立的。
  • 然而,对于引用类型 hobbies 列表,浅克隆只是复制了引用,这意味着原始对象和克隆对象中的 hobbies 指向同一个列表对象。
  • 当修改原始对象的 hobbies 列表内容(添加 “Drawing”)以及修改 age 为 35 时,由于克隆对象的 hobbies 引用与原始对象相同,所以克隆对象的 hobbies 列表也会显示添加后的内容。而克隆对象的 age 虽然在克隆时与原始对象相同,但由于基本数据类型是按值复制,所以不会随原始对象的修改而改变。

(二)深克隆示例

1. 为了实现深克隆,可以使用序列化的方式:

package com.jsglxx.design.prototype.deep;import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.List;class Person implements Serializable {private static final long serialVersionUID = 1L;private int age;private List<String> hobbies;public Person(int age, List<String> hobbies) {this.age = age;this.hobbies = hobbies;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public List<String> getHobbies() {return hobbies;}public void setHobbies(List<String> hobbies) {this.hobbies = hobbies;}public Person 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 (Person) ois.readObject();} catch (IOException | ClassNotFoundException e) {e.printStackTrace();return null;}}
}

2. 使用示例:

package com.jsglxx.design.prototype.deep;import java.util.ArrayList;
import java.util.List;public class DeepCloneExample {public static void main(String[] args) {try {List<String> hobbies = new ArrayList<>();hobbies.add("Reading");Person original = new Person(30, hobbies);Person clone = original.deepClone();System.out.println("Original: Age = " + original.getAge() + ", Hobbies = " + original.getHobbies());System.out.println("Clone: Age = " + clone.getAge() + ", Hobbies = " + clone.getHobbies());// 修改原始对象的引用类型字段内容original.getHobbies().add("Drawing");original.setAge(35);System.out.println("After modification:");System.out.println("Original: Age = " + original.getAge() + ", Hobbies = " + original.getHobbies());System.out.println("Clone: Age = " + clone.getAge() + ", Hobbies = " + clone.getHobbies());} catch (Exception e) {e.printStackTrace();}}
}

在这个深克隆示例中,

  1. 通过序列化的方式进行深克隆。首先将原始对象序列化为字节流,然后再从字节流中反序列化出一个新的对象作为克隆对象。
  2. 对于基本数据类型 age,其行为与浅克隆类似,被按值复制,所以原始对象和克隆对象的 age 是独立的。
  3. 对于引用类型 hobbies 列表,在深克隆过程中,实际上创建了一个新的列表对象,并将原始列表中的内容复制到新列表中。这意味着原始对象和克隆对象的 hobbies 列表是完全独立的两个对象。
  4. 当修改原始对象的 hobbies 列表内容(添加 “Drawing”)以及修改 age 为 35 时,由于克隆对象的 hobbies 列表是独立的新对象,所以不会受到原始对象修改的影响。而克隆对象的 age 也同样不会随原始对象的修改而改变。

四、原型模式的优缺点

(一)优点

  1. 性能优化:原型模式通过复制现有对象来创建新对象,避免了复杂的对象初始化过程,从而提高了对象创建的效率。特别是对于创建成本较高的对象,如需要大量计算或占用大量资源的对象,原型模式可以显著提高性能。
  2. 简化对象创建过程:在某些情况下,对象的创建过程可能非常复杂,需要多个步骤和大量的参数。使用原型模式可以将这些复杂的创建过程封装在原型对象中,通过复制原型对象来创建新对象,从而简化了对象创建的过程。
  3. 提供灵活性:原型模式允许在运行时动态地创建对象,并且可以根据需要修改原型对象,从而创建出不同的克隆对象。这种灵活性使得原型模式在一些需要动态创建对象的场景下非常有用,如用户界面开发、游戏开发等。

(二)缺点

  1. 违背开闭原则:为了实现原型模式,需要为每个类都提供一个clone()方法或者实现Serializable接口,这可能会导致代码的可维护性降低。特别是当需要修改类的结构时,可能需要同时修改clone()方法或者序列化过程,这违背了开闭原则。
  2. 深克隆实现复杂:深克隆需要复制对象及其引用的所有对象,这可能会导致实现复杂。特别是对于复杂的对象结构,深克隆可能需要递归地复制所有的引用对象,这可能会导致性能问题和代码的复杂性增加。
  3. 处理循环引用困难:如果对象之间存在循环引用,即一个对象引用了另一个对象,而另一个对象又引用了第一个对象,那么在进行深克隆时可能会出现问题。处理循环引用需要特殊的算法和技术,这可能会增加代码的复杂性。

五、原型模式的应用场景

(一)资源密集型对象创建

当创建对象的成本较高时,如需要大量计算、占用大量内存或需要访问外部资源等,可以使用原型模式来提高性能。通过复制已经存在的对象,可以避免重复的计算和资源访问,从而提高创建对象的效率。

例如,在图形绘制软件中,创建一个复杂的图形对象可能需要大量的计算和内存。如果每次需要绘制这个图形时都重新创建一个对象,将会非常耗时和耗资源。使用原型模式,可以将已经创建好的图形对象作为原型,通过复制原型来创建新的图形对象,从而提高绘制效率。

(二)复杂对象复制

当需要复制一个复杂的对象结构时,使用原型模式可以简化复制过程。通过复制原型对象,可以避免手动复制每个子对象的繁琐过程,并且可以确保复制的准确性。

例如,在一个游戏开发中,角色对象可能包含多个属性和子对象,如位置、速度、装备等。如果需要复制一个角色对象,可以使用原型模式来复制整个角色对象及其所有的子对象,从而确保复制的准确性和完整性。

(三)动态对象创建

在一些需要动态创建对象的场景下,如用户界面开发、游戏开发等,可以使用原型模式来提供灵活性。通过修改原型对象,可以创建出不同的克隆对象,从而满足不同的需求。

例如,在一个用户界面开发中,可能需要根据用户的选择动态地创建不同类型的窗口或控件。使用原型模式,可以将不同类型的窗口或控件作为原型,通过复制原型来创建新的窗口或控件,从而满足用户的需求。

六、总结

原型模式是一种非常有用的设计模式,它允许通过复制现有对象来创建新对象,从而提高对象创建的效率和灵活性。在 Java 中,可以通过浅克隆和深克隆两种方式实现原型模式。浅克隆只复制对象的基本数据类型字段和引用类型字段的引用,而深克隆则会复制对象及其引用的所有对象。原型模式具有性能优化、简化对象创建过程和提供灵活性等优点,但也存在违背开闭原则、深克隆实现复杂和处理循环引用困难等缺点。在实际应用中,需要根据具体情况选择合适的实现方式,并注意处理可能出现的问题。

🌟 对技术管理感兴趣 请关注下方 ⬇ 【 技术管理修行】

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

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

相关文章

c语言中整数在内存中的存储

整数的二进制表示有三种&#xff1a;原码&#xff0c;反码&#xff0c;补码 有符号的整数&#xff0c;三种表示方法均有符号位和数值位两部分&#xff0c;符号位都是用‘0’表示“正&#xff0c;用1表示‘负’ 最高位的以为被当作符号位&#xff0c;剩余的都是数值位。 整数…

python 制作 发货单 (生成 html, pdf)

起因&#xff0c; 目的: 某个小店&#xff0c;想做个发货单。 过程: 先写一个 html 模板。准备数据&#xff0c; 一般是从数据库读取&#xff0c;也可以是 json 格式&#xff0c;或是 python 字典。总之&#xff0c;是数据内容。使用 jinja2 来渲染模板。最终的结果可以是 h…

使用 telnet 连接 dubbo 服务调用暴露的 dubbo 接口

目录 前言 环境准备 Telnet客户端 zookeeper pom 配置文件 dubbo接口 telnet连接dubbo dubbo命令 ls invoke 前言 工作中的微服务项目远程调用使用的技术是 dubbo&#xff0c;当对外提供了一个 duboo 接口时&#xff0c;无论是开发阶段自测&#xff0c;还是上线了服…

【EndNote版】如何在Word中引用文献

1、在Word中&#xff0c;鼠标光标放在所需插入文献的位置 2、点击选项卡中的“EndNote X9”&#xff0c;直接在EndNote中选中对应的文献 3、选中文献&#xff0c;点击工具栏中的“引用” 4、最后就可在Word中看到所插入的文献

华为配置BFD状态与接口状态联动实验

组网图形 图1 配置BFD状态与接口状态联动组网图 BFD简介配置注意事项组网需求配置思路操作步骤配置文件 BFD简介 为了减小设备故障对业务的影响&#xff0c;提高网络的可靠性&#xff0c;网络设备需要能够尽快检测到与相邻设备间的通信故障&#xff0c;以便及时采取措施&…

技术成神之路:设计模式(二十三)解释器模式

相关文章&#xff1a;技术成神之路&#xff1a;二十三种设计模式(导航页) 介绍 解释器模式&#xff08;Interpreter Pattern&#xff09;是一种行为设计模式&#xff0c;用于定义一种语言的文法表示&#xff0c;并提供一个解释器来处理这种文法。它用于处理具有特定语法或表达…

【C++】继承与模板

继承 1.继承的概念 概念&#xff1a;继承(inheritace)机制是面向对象程序设计使代码可以复用的最重要的手段&#xff0c;它允许程序员在保持原有类特性的基础上进行扩展&#xff0c;增加功能&#xff0c;这样产生新的类&#xff0c;称之为派生类。继承呈现了面向对象程序设计的…

时序分解 | TTNRBO-VMD改进牛顿-拉夫逊算法优化变分模态分解

时序分解 | TTNRBO-VMD改进牛顿-拉夫逊算法优化变分模态分解 目录 时序分解 | TTNRBO-VMD改进牛顿-拉夫逊算法优化变分模态分解效果一览基本介绍程序设计参考资料 效果一览 基本介绍 (创新独家)TTNRBO-VMD改进牛顿-拉夫逊优化算优化变分模态分解TTNRBO–VMD 优化VMD分解层数K和…

设计模式-单例模型(单件模式、Singleton)

单例模式是一种创建型设计模式&#xff0c; 让你能够保证一个类只有一个实例&#xff0c; 并提供一个访问该实例的全局节点。 单例模式同时解决了两个问题&#xff0c; 所以违反了单一职责原则&#xff1a; 保证一个类只有一个实例。 为什么会有人想要控制一个类所拥有的实例…

【Java并发编程】信号量Semaphore详解

一、简介 Semaphore&#xff08;信号量&#xff09;&#xff1a;是用来控制同时访问特定资源的线程数量&#xff0c;它通过协调各个线程&#xff0c;以保证合理的使用公共资源。 Semaphore 一般用于流量的控制&#xff0c;特别是公共资源有限的应用场景。例如数据库的连接&am…

谈谈对函数式编程的理解及rxjs的使用

背景 函数式编程可以说是非常古老的编程方式&#xff0c;但是近几年变成了一个非常热门的话题。不管是Google力推的Go、学术派的Scala与Haskell&#xff0c;还是Lisp的新语言Clojure&#xff0c;这些新的函数式编程语言越来越受到人们的关注。函数式编程思想对前端的影响很大&…

C语言基础题(大合集2)

1. 时间转换 给定秒数 --> 输出秒数 转化成 时/分/秒 //时间转换 //给定秒数 --> 转换成 小时/分/秒 int main() {//输入int seconds 0;int h 0;//小时int m 0;//分钟int s 0;//秒scanf("%d", &seconds);//计算h seconds / 60 / 60;m seconds / 60…

详解varint,zigzag编码, 以及在Go标准库中的实现

文章目录 为啥需要varint编码为啥需要zigzag编码varint编码解码 zigzag编码解码 局限性 为啥需要varint编码 当我们用定长数字类型int32来表示整数时&#xff0c;为了传输一个整数1&#xff0c;我们需要传输00000000 00000000 00000000 00000001 32 个 bits&#xff0c;而有价…

【C++】STL初识

【C】STL初识 文章目录 【C】STL初识前言一、STL基本概念二、STL六大组件简介三、STL三大组件四、初识STL总结 前言 本篇文章将讲到STL基本概念&#xff0c;STL六大组件简介&#xff0c;STL三大组件&#xff0c;初识STL。 一、STL基本概念 STL(Standard Template Library,标准…

QT建立工程时出现了:Reading Project

QT建立工程时出现了&#xff1a;Reading Project 打开建立的工程发现&#xff0c;缺少build文件 从别的工程中复制一个build&#xff0c;点击.pro就可以打开了

【CSS3】css开篇基础(4)

1.❤️❤️前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; Hello, Hello~ 亲爱的朋友们&#x1f44b;&#x1f44b;&#xff0c;这里是E绵绵呀✍️✍️。 如果你喜欢这篇文章&#xff0c;请别吝啬你的点赞❤️❤️和收藏&#x1f4d6;&#x1f4d6;。如果你对我的…

Spring Boot实现的动态化酒店住宿管理系统

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理酒店客房管理系统的相关信息成为必然。开发…

图文详解ChatGPT-o1完成论文写作的全流程

学境思源&#xff0c;一键生成论文初稿&#xff1a; AcademicIdeas - 学境思源AI论文写作 本月中旬OpenAI发布了OpenAI o1系列新的AI模型。 据OpenAI介绍&#xff0c;这些模型旨在花更多时间思考后再做出反应&#xff0c;就像人一样。通过训练&#xff0c;它们学会改进思维过…

深度学习(六)CNN:图像处理的强大工具(6/10)

一、CNN 的概述 卷积神经网络&#xff08;Convolutional Neural Networks&#xff0c;CNN&#xff09;是深度学习的代表算法之一&#xff0c;在深度学习中占据着重要地位。 CNN 的发展历程可追溯至 20 世纪 80 至 90 年代&#xff0c;时间延迟网络和 LeNet - 5 是最早出现的卷…

conda虚拟环境中安装cuda方法、遇到的问题

conda虚拟环境中安装cuda方法、遇到的问题 文章目录 conda虚拟环境中安装cuda方法、遇到的问题conda虚拟环境中安装cudacuda.h和cuda_runtime.hpytorch运行时的CUDA版本其他问题检查包冲突nvcc -V和nvidia-smi显示的版本不一致cuda路径 conda虚拟环境中安装cuda 参考文章&…