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

二、原型模式

文章目录

  • 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 总结

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

相关文章:

二、原型模式

文章目录 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 中…...

【目标检测】Anaconda+PyTorch(GPU)+PyCharm(Yolo5)配置

前言 本文主要介绍在windows系统上的Anaconda、PyTorch、PyCharm、Yolov5关键步骤安装&#xff0c;为使用yolo所需的环境配置完善。同时也算是记录下我的配置流程&#xff0c;为以后用到的时候能笔记查阅。 Anaconda 软件安装 Anaconda官网&#xff1a;https://www.anaconda…...

Django实战项目之进销存数据分析报表——第二天:项目创建和 PyCharm 配置

在上一篇博客中&#xff0c;我们讨论了如何搭建一个全栈 Web 应用的开发环境&#xff0c;包括 Python 环境的创建、Django 和 MySQL 的安装以及前端技术栈的选择。现在&#xff0c;让我们继续深入&#xff0c;学习如何在 PyCharm 中创建一个新的 Django 项目并进行配置。 一…...

静态路由实验

1.实验拓扑图 二、实验要求 1.R6为ISP&#xff0c;接口IP地址均为公有地址&#xff0c;该设备只能配置IP地址&#xff0c;之后不能再对其进行任何配置&#xff1b; 2.R1-R5为局域网&#xff0c;私有IP地址192.168.1.0/24&#xff0c;请合理分配&#xff1b; 3.R1、R2、R4&…...

VSCode STM32嵌入式开发插件记录

要卸载之前搭建的VSCode嵌入式开发环境了&#xff0c;记录一下用的插件。 1.Cortex-Debug https://github.com/Marus/cortex-debug 2.Embedded IDE https://github.com/github0null/eide 3.Keil uVision Assistant https://github.com/jacksonjim/keil-assistant/ 4.RTO…...

linux cpu 占用超100% 分析。

感谢: https://www.cnblogs.com/wolfstark/p/16450131.html 总结&#xff1a; 查看进程中各个线程占用百分比 top -H -p <pid> 某线程100%了 说明 任务处理不过来 会卡 但是永远不可能超100% 系统监视器里面看到的是 所有线程占用的 总和会超100%。 所以最好的情况是&…...

自然学习法和科学学习法

一、自然学习法 自然学习法&#xff1a;什么事自然学习法&#xff0c;特意让kimi来回答了一下。所谓的自然学习法说的俗一点就是野路子学习方法。这种学习方法的特点是“慢”“没有系统性”&#xff0c;学完之后感觉都会了&#xff0c;但是又感觉什么都不会。 二、科学学习法 …...

力扣第二十四题——两两交换链表中的节点

内容介绍 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换&#xff09;。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4] 输出&#xff…...

C语言柔性数组详解

目录 1.柔性数组 2.柔性数组的特点 3.柔性数组的使用 4.柔性数组的优势 1.柔性数组 C99 中&#xff0c;结构体中的最后一个元素允许是未知大小的数组&#xff0c;这就叫做『柔性数组』成员。 例如&#xff1a; struct S {char c;int n;int arr[];//柔性数组 }; struct …...

自动驾驶---视觉Transformer的应用

1 背景 在过去的几年&#xff0c;随着自动驾驶技术的不断发展&#xff0c;神经网络逐渐进入人们的视野。Transformer的应用也越来越广泛&#xff0c;逐步走向自动驾驶技术的前沿。笔者也在博客《人工智能---什么是Transformer?》中大概介绍了Transformer的一些内容&#xff1a…...

预训练语言模型实践笔记

Roberta output_hidden_statesTrue和last_hidden_states和pooler_output 在使用像BERT或RoBERTa这样的transformer模型时&#xff0c;output_hidden_states和last_hidden_state是两个不同的概念。 output_hidden_states: 这是一个布尔值&#xff0c;决定了模型是否应该返回所…...

Perl 哈希

Perl 哈希 Perl 哈希是一种强大的数据结构&#xff0c;用于存储键值对集合。它是 Perl 语言的核心特性之一&#xff0c;广泛应用于各种编程任务中。本文将详细介绍 Perl 哈希的概念、用法和最佳实践。 什么是 Perl 哈希&#xff1f; Perl 哈希是一种关联数组&#xff0c;其中…...

Linux之Mysql索引和优化

一、MySQL 索引 索引作为一种数据结构,其用途是用于提升数据的检索效率。 1、索引分类 - 普通索引(INDEX):索引列值可重复 - 唯一索引(UNIQUE):索引列值必须唯一,可以为NULL - 主键索引(PRIMARY KEY):索引列值必须唯一,不能为NULL,一个表只能有一个主键索引 - 全…...

springboot业务逻辑写在controller层吗

Spring Boot中的业务逻辑不应该直接写在Controller层。‌ 在Spring Boot项目中&#xff0c;‌通常将业务逻辑分为几个层次&#xff0c;‌包括Controller层、‌Service层、‌Mapper层和Entity层。‌ 1.其中&#xff0c;‌Controller层主要负责处理HTTP请求&#xff0c;‌通过注…...

Ubuntu 24.04 LTS 桌面安装MT4或MT5 (MetaTrader)教程

运行脚本即可在 Ubuntu 24.04 LTS Noble Linux 上轻松安装 MetaTrader 5 或 4 应用程序&#xff0c;使用 WineHQ 进行外汇交易。 MetaTrader 4 (MT4) 或 MetaTrader 5 是用于交易外汇对和商品的流行平台。它支持各种外汇经纪商、内置价格分析工具以及通过专家顾问 (EA) 进行自…...

Go基础编程 - 12 -流程控制

流程控制 1. 条件语句1.1. if...else 语句1.2. switch 语句1.3. select 语句1.3.1. select 语句的通信表达式1.3.2. select 的基特性1.3.3. select 的实现原理1.3.4. 经典用法1.3.4.1 超时控制1.3.4.2 多任务并发控制1.3.4.3 监听多通道消息1.3.4.4 default 实现非堵塞读写 2. …...

汽车信息安全--TLS,OpenSSL

目录 TLS相关知识 加密技术 对称加密 非对称加密 数字签名和CA 信任链 根身份证和自签名 双方TLS认证 加密和解密的性能 TLS相关知识 加密技术 TLS依赖两种加密技术 1. 对称加密&#xff08;symmetric encryption&#xff09; 2. 非对称加密&#xff08;asymmetri…...

深入探索 SQL 中的 LIKE 右模糊匹配(LIKE RIGHT)与左模糊匹配(LIKE LEFT)

引言 在数据库操作中&#xff0c;LIKE 子句是执行模糊搜索的强大工具&#xff0c;用于匹配列中的数据与指定的模式。本文将详细介绍 LIKE 子句中的两种常用模式&#xff1a;右模糊匹配&#xff08;LIKE RIGHT&#xff09;和左模糊匹配&#xff08;LIKE LEFT&#xff09;&#…...

mybatis 多数据源 TDataSource required a single bean, but 2 were found

情况说明&#xff1a; 项目中本来就有一个数据源了&#xff0c;运行的好好的后来又合并了另一个项目&#xff0c;另一个项目也配置了数据源。 于是出现了如下错误&#xff1a; mybatis 多数据源 TDataSource required a single bean, but 2 were found 解决方法&#xff1a…...

Dubbo SPI 之路由器

1. 背景介绍 Dubbo 是一个高性能的 Java RPC 框架&#xff0c;由阿里巴巴开源并广泛应用于分布式系统中。在 Dubbo 的架构中&#xff0c;SPI&#xff08;Service Provider Interface&#xff09;是一个关键组件&#xff0c;允许在运行时动态加载不同的服务实现。SPI 机制提供了…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度​

一、引言&#xff1a;多云环境的技术复杂性本质​​ 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时&#xff0c;​​基础设施的技术债呈现指数级积累​​。网络连接、身份认证、成本管理这三大核心挑战相互嵌套&#xff1a;跨云网络构建数据…...

【网络】每天掌握一个Linux命令 - iftop

在Linux系统中&#xff0c;iftop是网络管理的得力助手&#xff0c;能实时监控网络流量、连接情况等&#xff0c;帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...

C++实现分布式网络通信框架RPC(3)--rpc调用端

目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中&#xff0c;我们已经大致实现了rpc服务端的各项功能代…...

DAY 47

三、通道注意力 3.1 通道注意力的定义 # 新增&#xff1a;通道注意力模块&#xff08;SE模块&#xff09; class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...

React Native在HarmonyOS 5.0阅读类应用开发中的实践

一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强&#xff0c;React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 &#xff08;1&#xff09;使用React Native…...

el-switch文字内置

el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...

ETLCloud可能遇到的问题有哪些?常见坑位解析

数据集成平台ETLCloud&#xff0c;主要用于支持数据的抽取&#xff08;Extract&#xff09;、转换&#xff08;Transform&#xff09;和加载&#xff08;Load&#xff09;过程。提供了一个简洁直观的界面&#xff0c;以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...

C# 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...

从 GreenPlum 到镜舟数据库:杭银消费金融湖仓一体转型实践

作者&#xff1a;吴岐诗&#xff0c;杭银消费金融大数据应用开发工程师 本文整理自杭银消费金融大数据应用开发工程师在StarRocks Summit Asia 2024的分享 引言&#xff1a;融合数据湖与数仓的创新之路 在数字金融时代&#xff0c;数据已成为金融机构的核心竞争力。杭银消费金…...

Chromium 136 编译指南 Windows篇:depot_tools 配置与源码获取(二)

引言 工欲善其事&#xff0c;必先利其器。在完成了 Visual Studio 2022 和 Windows SDK 的安装后&#xff0c;我们即将接触到 Chromium 开发生态中最核心的工具——depot_tools。这个由 Google 精心打造的工具集&#xff0c;就像是连接开发者与 Chromium 庞大代码库的智能桥梁…...