二、原型模式

文章目录

  • 1 基本介绍
  • 2 实现方式
    • 深浅拷贝
    • 目标
    • 2.1 使用 Object 的 clone() 方法
      • 2.1.1 代码
      • 2.1.2 特性
      • 2.1.3 实现深拷贝
    • 2.2 在 clone() 方法中使用序列化
      • 2.2.1 代码
    • 2.2.2 特性
  • 3 实现的要点
  • 4 Spring 中的原型模式
  • 5 原型模式的类图及角色
    • 5.1 类图
      • 5.1.1 不限制语言
      • 5.1.2 在 Java 中的类图
    • 5.2 角色
      • 5.2.1 Prototype ( 原型 )
      • 5.2.2 ConcretePrototype ( 具体的原型 )
      • 5.2.3 Client ( 使用者 )
  • 6 原型模式的优缺点
  • 7 原型模式的使用场景
  • 8 总结

1 基本介绍

原型模式(Prototype Pattern)是一种创建型设计模式,它允许 通过复制已有的对象来创建新的对象,而无需知道对象创建的细节

2 实现方式

深浅拷贝

原型模式的实现围绕着 深浅拷贝 的特性展开,其定义如下:

  • 浅拷贝(Shallow Copy):只复制对象的 第一层 属性(即 八大基本数据类型 + String 这个引用数据类型),对于引用类型的属性(除了字符串 String 类型外),复制的是内存地址引用,而非对象本身。它存在这种危险:如果原对象的引用类型属性被修改,浅拷贝得到的对象的对应属性也会受到影响
  • 深拷贝(Deep Copy):递归地 复制对象及其所有子对象,创建一个全新的对象,与原对象没有任何关联。

目标

实现一个 Sheep 类,字段为 String nameint age,它能通过 clone() 方法克隆出一模一样的对象。

共有以下 2 种实现:

2.1 使用 Object 的 clone() 方法

2.1.1 代码

public class Sheep implements Cloneable { // 实现了 Cloneable 接口private String name;private int age;private String friend;public Sheep(String name, int age, String friend) {this.name = name;this.age = age;this.friend = friend;}public String getName() {return name;}public int getAge() {return age;}public String getFriend() {return friend;}@Overridepublic Sheep clone() {try {return (Sheep) super.clone(); // 使用 Object 的 clone()} catch (Exception e) { // 如果有异常e.printStackTrace(); // 打印异常return null; // 并返回 null}}public static void main(String[] args) { // 测试程序Sheep sheep = new Sheep("silvery", 6, new Sheep("gold", 13, null));Sheep clone = sheep.clone();System.out.println(sheep == clone);System.out.println("[sheep]: name = " + sheep.getName() + ", age = " + sheep.getAge()+ ", friend = " + sheep.getFriend() + ", friend.name = " + sheep.getFriend().getName());System.out.println("[clone]: name = " + clone.getName() + ", age = " + clone.getAge()+ ", friend = " + clone.getFriend() + ", friend.name = " + clone.getFriend().getName());/* 测试结果如下:false[sheep]: name = silvery, age = 6, friend = cn.me.Sheep@f6f4d33, friend.name = gold[clone]: name = silvery, age = 6, friend = cn.me.Sheep@f6f4d33, friend.name = gold总结:sheep 和 name 不是同一个对象,但属性一样它们的 friend 是同一个 friend,这就是 浅拷贝*/}
}

2.1.2 特性

  • 浅拷贝Object 自带的 clone() 方法是浅拷贝,对于非 String 类型的引用类型,它只能拷贝其内存地址。
  • 实现简单:虽然这种方式是浅拷贝,但实现起来很简单,如果能确定不使用除 String 类型之外的引用类型,这种方式很方便。

2.1.3 实现深拷贝

如果一个类中除 String 类型之外的引用类型 很少,那么可以像以下这样 单独 对这些引用类型进行 克隆,从而实现深拷贝:

@Override
public Sheep clone() {try {Sheep clone = (Sheep) super.clone();// 对 除 String 之外的引用类型 单独克隆// 如果没有判断空值,则可能会抛出空指针异常 NullPointerExceptionif (clone.friend != null) {clone.friend = this.friend.clone();}return clone;} catch (Exception e) { // 如果有异常e.printStackTrace(); // 打印异常return null; // 并返回 null}
}

2.2 在 clone() 方法中使用序列化

2.2.1 代码

注意:如果要使用序列化,则要实现 Serializable 接口

public class Sheep implements Cloneable, Serializable { // 实现了 Cloneable 和 Serializable 接口private String name;private int age;private String friend;public Sheep(String name, int age, String friend) {this.name = name;this.age = age;this.friend = friend;}public String getName() {return name;}public int getAge() {return age;}public String getFriend() {return friend;}@Overridepublic Sheep clone() {ByteArrayOutputStream bos = null; // 字节数组输出流ObjectOutputStream oos = null; // 对象输出流ByteArrayInputStream bis = null; // 字节数组输入流ObjectInputStream ois = null; // 对象输入流try {// 序列化:将当前对象以对象流的形式输出bos = new ByteArrayOutputStream();oos = new ObjectOutputStream(bos);oos.writeObject(this);// 反序列化:将当前对象以对象流的形式读取,形成一个新的对象bis = new ByteArrayInputStream(bos.toByteArray());ois = new ObjectInputStream(bis);Object clone = ois.readObject();return (Sheep) clone;} catch (Exception e) { // 如果有异常e.printStackTrace(); // 打印异常return null; // 并返回 null} finally {try { // 用完流之后记得关闭if (bos != null) {bos.close();}if (oos != null) {oos.close();}if (bis != null) {bis.close();}if (ois != null) {ois.close();}} catch (Exception e) { // 如果关闭流出现异常e.printStackTrace(); // 则打印异常}}}public static void main(String[] args) { // 测试程序Sheep sheep = new Sheep("silvery", 6, new Sheep("gold", 13, null));Sheep clone = sheep.clone();System.out.println(sheep == clone);System.out.println("[sheep]: name = " + sheep.getName() + ", age = " + sheep.getAge()+ ", friend = " + sheep.getFriend() + ", friend.name = " + sheep.getFriend().getName());System.out.println("[clone]: name = " + clone.getName() + ", age = " + clone.getAge()+ ", friend = " + clone.getFriend() + ", friend.name = " + clone.getFriend().getName());/* 测试结果如下:false[sheep]: name = silvery, age = 6, friend = cn.me.Sheep@421faab1, friend.name = gold[clone]: name = silvery, age = 6, friend = cn.me.Sheep@504bae78, friend.name = gold总结:sheep 和 name 不是同一个对象,但属性一样它们的 friend 不是同一个 friend,但属性一样,这是 深拷贝*/}
}

2.2.2 特性

  • 深拷贝:这种方式是深拷贝,它会递归地构建所有对象,从而创建一个与原对象无任何关联的新对象,只是属性相同罢了。
  • 实现复杂:这种方式的实现很复杂,涉及到四个流的创建、使用和关闭,要处理的异常也很多。
  • 性能开销:这种方式由于需要创建、使用和关闭流,所以可能会消耗一部分性能。

3 实现的要点

  • 实现 Cloneable 接口。
  • 重写 Object 类的 clone() 方法。
  • 考虑深浅拷贝的问题。

4 Spring 中的原型模式

对于 ClassPathXmlApplicationContext 类的对象,它的 getBean() 方法继承自 AbstractBeanFactory 抽象类的 getBean() 方法,其内部调用了 doGetBean() 方法,在 doGetBean() 方法中使用了原型模式(这里面具体的逻辑就很复杂了),如下所示:

protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args,boolean typeCheckOnly) throws BeansException {// ...else if (mbd.isPrototype()) {prototypeInstance = null;Object prototypeInstance;try {this.beforePrototypeCreation(beanName);prototypeInstance = this.createBean(beanName, mbd, args); // 在这里使用了原型模式} finally {this.afterPrototypeCreation(beanName);}beanInstance = this.getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);}// ...
}

5 原型模式的类图及角色

5.1 类图

5.1.1 不限制语言

alt text

5.1.2 在 Java 中的类图

alt text

5.2 角色

5.2.1 Prototype ( 原型 )

该角色负责 定义 用于复制现有实例来生成新实例的 方法,在 Java 中一般指的是 Object 类,它定义了 nativeclone() 方法。

5.2.2 ConcretePrototype ( 具体的原型 )

该角色负责 实现 用于复制现有实例来生成新实例的 方法,在 Java 中不仅要继承 Object 类(如果一个类不指定继承的类,则它默认继承 Object 类),还要实现 Cloneable 这个 标记接口,虽然它没有定义方法,但要使用 clone() 方法就得实现它,否则就会抛出 CloneNotSupportedException (不支持 clone() 方法)的异常。

5.2.3 Client ( 使用者 )

该角色负责 使用 用于复制现有实例来生成新实例的 方法

6 原型模式的优缺点

优点

  • 性能高:使用原型模式复用的方式创建实例对象,比使用构造器重新创建对象性能要高。
  • 流程简单:原型模式可以简化创建的过程,可以直接修改现有的对象实例的值,达到复用的目的。
  • 具有动态性:使用构造器创建对象的代码在运行期间时固定的,而使用原型模式可以获取对象运行时的属性,从而 动态地 创建出新的对象。

缺点

  • 实现复杂:必须重写对象的 clone() 方法,且需要考虑 深拷贝浅拷贝 的风险。
  • 克隆方法需要通盘考虑:配备克隆方法需要对类的功能进行通盘考虑,特别是对于已有的类,可能需要修改其结构以支持克隆,违背了 开闭原则

7 原型模式的使用场景

  • 对象的创建成本较高:如果创建对象的过程比较复杂或耗时较长,可以使用原型模式通过复制一个现有对象的属性和方法来创建新对象,从而避免昂贵的创建过程。
  • 需要创建大量相似的对象:如果需要创建大量相似的对象,可以先创建一个原型对象,然后通过复制原型对象来创建新对象,从而提高对象创建的效率和性能。
  • 对象的修改频繁:如果对象的属性需要经常变化,而且每次变化都需要创建一个新的对象,可以使用原型模式,通过复制原型对象来创建新对象并修改其属性,而不需要每次都重新创建新对象。
  • 隐藏对象的创建细节:如果创建对象的细节比较复杂,不希望客户端直接与创建对象的过程耦合,可以使用原型模式,客户端只需要通过复制一个已有对象来创建新对象,而无需知道创建的细节。

8 总结

原型模式是一种非常有用的 创建型 设计模式,它 通过复制已有的对象来创建新对象,从而 提高了对象创建的效率和性能。然而,在使用时需要注意 深浅拷贝问题 以及 克隆方法的实现细节,以避免出现不必要的错误。

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

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

相关文章

免费【2024】springboot 趵突泉景区的智慧导游小程序

博主介绍&#xff1a;✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌ 技术范围&#xff1a;SpringBoot、Vue、SSM、HTML、Jsp、PHP、Nodejs、Python、爬虫、数据可视化…

开发桌面程序-Electron入门

Electron是什么 来自官网的介绍 Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 嵌入 Chromium 和 Node.js 到 二进制的 Electron 允许您保持一个 JavaScript 代码代码库并创建 在Windows上运行的跨平台应用 macOS和Linux——不需要本地开发 经验。 总…

普中51单片机:DS1302时钟芯片讲解与应用(十)

文章目录 引言基本特性什么是RAM&#xff1f;什么是涓流充电&#xff1f; 电路图和引脚说明通信协议以及工作流程寄存器控制寄存器日历/时钟寄存器 DS1302读写时序代码演示——数码管显示时分秒 引言 DS1302 是一款广泛使用的实时时钟 (RTC) 芯片&#xff0c;具有低功耗、内置…

本地部署VMware ESXi服务实现无公网IP远程访问管理服务器

文章目录 前言1. 下载安装ESXi2. 安装Cpolar工具3. 配置ESXi公网地址4. 远程访问ESXi5. 固定ESXi公网地址 前言 在虚拟化技术日益成熟的今天&#xff0c;VMware ESXi以其卓越的性能和稳定性&#xff0c;成为了众多企业构建虚拟化环境的首选。然而&#xff0c;随着远程办公和跨…

《昇思25天学习打卡营第19天|基于MobileNetv2的垃圾分类》

基于MobileNetv2的垃圾分类 本文档主要介绍垃圾分类代码开发的方法。通过读取本地图像数据作为输入&#xff0c;对图像中的垃圾物体进行检测&#xff0c;并且将检测结果图片保存到文件中。 1、实验目的 了解熟悉垃圾分类应用代码的编写&#xff08;Python语言&#xff09;&a…

机器学习 | 回归算法原理——多项式回归

Hi&#xff0c;大家好&#xff0c;我是半亩花海。接着上次的最速下降法&#xff08;梯度下降法&#xff09;继续更新《白话机器学习的数学》这本书的学习笔记&#xff0c;在此分享多项式回归这一回归算法原理。本章的回归算法原理基于《基于广告费预测点击量》项目&#xff0c;…

html+css+js前端作业 王者荣耀官网5个页面带js

htmlcssjs前端作业 王者荣耀官网5个页面带js 下载地址 https://download.csdn.net/download/qq_42431718/89574989 目录1 目录2 目录3 项目视频 王者荣耀5个页面&#xff08;带js&#xff09; 页面1 页面2 页面3 页面4 页面5

分布式Apollo配置中心搭建实战

文章目录 环境要求第一步、软件下载第二步、创建数据库参考文档 最近新项目启动&#xff0c;采用Apollo作为分布式的配置中心&#xff0c;在本地搭建huanj 实现原理图如下所示。 环境要求 Java版本要求&#xff1a;JDK1.8 MySql版本要求&#xff1a;5.6.5 Apollo版本要求&…

机器学习(二十):偏差和方差问题

一、判断偏差和方差 以多项式回归为例&#xff0c;红点为训练集数据&#xff0c;绿点为交叉验证数据。 下图的模型&#xff0c;训练集误差大&#xff0c;交叉验证集误差大&#xff0c;这代表偏差很大 下图的模型&#xff0c;训练集误差小&#xff0c;交叉验证集误差小&#x…

SpringBoot上传超大文件导致OOM,完美问题解决办法

问题描述 报错: Caused by: java.lang.OutOfMemoryError at java.io.ByteArrayOutputStream.hugeCapacity(ByteArrayOutputStream.java:123) ~[?:1.8.0_381] at java.io.ByteArrayOutputStream.grow(ByteArrayOutputStream.java:117) ~[?:1.8.0_381] at java.…

Windows下载、安装、部署Redis服务的详细流程

本文介绍在Windows电脑中&#xff0c;下载、安装、部署并运行Redis数据库服务的方法。 Redis&#xff08;Remote Dictionary Server&#xff09;是一个开源、高性能的键值存储系统&#xff0c;最初由Salvatore Sanfilippo在2009年发布&#xff0c;并由Redis Labs维护。Redis因其…

【word转pdf】【最新版本jar】Java使用aspose-words实现word文档转pdf

【aspose-words-22.12-jdk17.jar】word文档转pdf 前置工作1、下载依赖2、安装依赖到本地仓库 项目1、配置pom.xml2、配置许可码文件&#xff08;不配置会有水印&#xff09;3、工具类4、效果 踩坑1、pdf乱码2、word中带有图片转换 前置工作 1、下载依赖 通过百度网盘分享的文…

Docker Desktop安装

0 Preface/Foreward 0.1 参考文档 Overview of Docker Desktop | Docker Docs &#xff08;Docker Desktop使用手册&#xff09; 0.1.1 Docker Dashboard Before going any further, we want to highlight the Docker Dashboard, which gives you a quick view of the cont…

vue3+element-plus 实现动态菜单和动态路由的渲染

在 Vue.js 中&#xff0c;使用 Vue Router 管理路由数据&#xff0c;并将其用于渲染 el-menu&#xff08;Element UI 的菜单组件&#xff09;通常涉及以下几个步骤&#xff1a; 定义路由元数据&#xff1a; 在你的路由配置中&#xff0c;为每个路由项添加 meta 字段&#xff0c…

RAG vs 微调:大模型知识的进化之路

2024年&#xff0c;大模型应用落地迎来全面提速。越来越多的企业在加大对大模型的投入&#xff0c;抢抓变革机遇&#xff0c;加速应用落地。大模型应用落地绕不开的两个关键词“RAG”和“微调”。那么什么是RAG&#xff1f;什么是大模型微调&#xff1f;大模型项目建设中RAG和微…

redis的使用场景和持久化方式

redis的使用场景 热点数据的缓存。热点&#xff1a;频繁读取的数据。限时任务的操作&#xff1a;短信验证码。完成session共享的问题完成分布式锁。 redis的持久化方式 什么是持久化&#xff1a;把内存中的数据存储到磁盘的过程&#xff0c;同时也可以把磁盘中的数据加载到内存…

基于Jeecgboot3.6.3的vue3版本前后端分离的流程管理平台

声明一下&#xff1a;因为这个项目license问题无法开源&#xff0c;更多技术支持与服务联系本人或加入我的知识星球提供一些技术服务。 初步完成了基于jeecgboot3.6.3的vue3版本的前后端流程管理平台&#xff0c;基于flowable6.8.1&#xff0c;同时支持bpmn流程设计器与仿钉钉流…

IT程序员的雷区都有哪些?你踩了哪些?

软件行业的工作经验和从事这个行业的工作年限直接相关。但从事这项工作的年限并不一定代表获得了相同年限的工作经验。 根据开发人员的习惯可以非常明显地分辨出谁更专业谁更业余。深入剖析下业余程序开发人员的种种表现&#xff0c;每个程序开发人员都应该引以为戒&#xff0…

怎样在 Nginx 中配置基于请求客户端屏幕尺寸的路由?

&#x1f345;关注博主&#x1f397;️ 带你畅游技术世界&#xff0c;不错过每一次成长机会&#xff01; 文章目录 怎样在 Nginx 中配置基于请求客户端屏幕尺寸的路由&#xff1f;一、为什么要基于屏幕尺寸进行路由&#xff1f;二、准备工作三、获取客户端屏幕尺寸信息四、配置…

经纬恒润与奇瑞汽车签订新能源项目重点供应商合作协议,共同开启合作新篇章

近日&#xff0c;2024年国家级芜湖经开区汽车零部件生态大会成功举行&#xff0c;经纬恒润受邀出席&#xff0c;与行业各伙伴齐聚经开区&#xff0c;同绘发展蓝图&#xff0c;助力经开区汽车产业高质量发展。会上&#xff0c;经纬恒润与奇瑞汽车签署合作协议&#xff0c;成为奇…