原型模式(创建型)

一、前言

        原型模式是一种创建型设计模式,它允许在运行时通过克隆现有对象来创建新对象,而不是通过常规的构造函数创建。在原型模式中,一个原型对象可以克隆自身来创建新的对象,这个过程可以通过深度克隆或浅克隆来实现。简单说原型模式其实就是从一个对象再创建另外一个可定制的对象,而且不需知道任何创建的细节。

        原型模式包含三个角色:

        ①Prototype(抽象原型类):定义用于克隆自身的接口,通常是一个抽象类或接口,其中声明了一个克隆方法 clone(),用于创建一个新的对象,具体的克隆操作由子类实现。

        ②ConcretePrototype(具体原型类):实现 Prototype 接口,实现 clone() 方法,完成对象的克隆操作。每个具体原型类都有自己的一些属性和方法,不同的具体原型类之间具有不同的实现方式。

        ③Client(客户类):使用原型模式创建新的对象,在原型模式中,客户端通过克隆接口来创建新的对象,而不是通过实例化的方式。客户端需要获取一个原型对象,并通过克隆方法来创建新的对象,从而避免了创建新对象的开销。

        原型模式的克隆分为浅拷贝和深拷贝两种。

二、浅拷贝

        创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。

        这里抽象原型类不用自己创建,都是Object,里面有clone()方法:

        具体原型类实现Cloneable,里面重写clone方法,直接super调用父类的clone方法即可:

import java.util.List;/*** @Author dengyifan* @create 2023/11/10 10:19* @description*/
public class UserPrototype implements Cloneable{private String name;private Integer age;private List<String> messages;public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public List<String> getMessages() {return messages;}public void setMessages(List<String> messages) {this.messages = messages;}@Overrideprotected UserPrototype clone() throws CloneNotSupportedException {return (UserPrototype) super.clone();}
}

        客户类:

import java.util.ArrayList;
import java.util.List;/*** @Author dengyifan* @create 2023/11/10 10:22* @description*/
public class PrototypeClient {public static void main(String[] args) throws CloneNotSupportedException {UserPrototype userPrototype1 = new UserPrototype();userPrototype1.setName("tom");userPrototype1.setAge(18);List<String> messages1 = new ArrayList<>();messages1.add("test");userPrototype1.setMessages(messages1);UserPrototype userPrototype2 = userPrototype1.clone();System.err.println(String.format("两个对象是否一致:%s", userPrototype1 == userPrototype2));System.err.println(String.format("两个对象的name属性是否一致:%s", userPrototype1.getName() == userPrototype2.getName()));System.err.println(String.format("两个对象的messages属性是否一致:%s", userPrototype1.getMessages() == userPrototype2.getMessages()));System.err.println(userPrototype1.getMessages());System.err.println(userPrototype2.getMessages());messages1.clear();messages1.add("test1");System.err.println(userPrototype1.getMessages());System.err.println(userPrototype2.getMessages());}
}

        运行结果:

        通过结果可以看到,通过克隆的对象与原型对象的引用不一致,说明再内存中存在两个不同的对象,一个是原型对象,一个是克隆生成的对象。而这里属性都显示是一致的,结果都是true,说明两个对象的成员对象是同一个,也就是对象本身是复制了,但是其成员的对象再内存中没有复制。并且我们修改原型的messages属性,克隆对象的messages也跟着改变,说明属性确实是同一个对象的引用。

三、深拷贝

        深拷贝中除了原型对象与克隆生成的对象被复制以外,里面的属性也需要被复制,即地址都不一样。这里深拷贝实现了Serializable,即进行了序列化。

import java.io.*;
import java.util.List;/*** @Author dengyifan* @create 2023/11/10 15:43* @description*/
public class UserPrototypeDeep implements Serializable {private String name;private Integer age;private List<String> messages;public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public List<String> getMessages() {return messages;}public void setMessages(List<String> messages) {this.messages = messages;}protected UserPrototypeDeep deepClone() throws Exception{ByteArrayOutputStream bao=new ByteArrayOutputStream();ObjectOutputStream oos=new ObjectOutputStream(bao);oos.writeObject(this);//将对象从流中取出ByteArrayInputStream bis=new ByteArrayInputStream(bao.toByteArray());ObjectInputStream ois=new ObjectInputStream(bis);return (UserPrototypeDeep) ois.readObject();}
}
import java.util.ArrayList;
import java.util.List;/*** @Author dengyifan* @create 2023/11/10 15:46* @description*/
public class PrototypeDeepClient {public static void main(String[] args) throws Exception {UserPrototypeDeep userPrototypeDeep1 = new UserPrototypeDeep();userPrototypeDeep1.setAge(19);userPrototypeDeep1.setName("tom");List<String> messages1 = new ArrayList<>();messages1.add("test");userPrototypeDeep1.setMessages(messages1);UserPrototypeDeep userPrototypeDeep2 = userPrototypeDeep1.deepClone();System.err.println(String.format("两个对象是否一致:%s", userPrototypeDeep1 == userPrototypeDeep2));System.err.println(String.format("两个对象的name属性是否一致:%s", userPrototypeDeep1.getName() == userPrototypeDeep2.getName()));System.err.println(String.format("两个对象的messages属性是否一致:%s", userPrototypeDeep1.getMessages() == userPrototypeDeep2.getMessages()));System.err.println(userPrototypeDeep1.getMessages());System.err.println(userPrototypeDeep2.getMessages());messages1.clear();messages1.add("test1");System.err.println(userPrototypeDeep1.getMessages());System.err.println(userPrototypeDeep2.getMessages());}
}

        运行结果:

        可以看出除了对象本身不一致以外,里面的属性也不一致,当修改原型对象里面引用对象的属性时,克隆对象的属性不会发生变化,即也证明属性不是同一个对象。

        对于原型模式的应用场景,主要有以下几点:

1. 当对象的创建过程非常复杂或耗费大量资源时,可以使用原型模式来复制一个现有对象,从而避免重复创建对象。

2. 当需要创建多个相似的对象时,可以使用原型模式来减少重复代码,提高代码的重用性。

3. 当创建对象的过程需要大量的数据准备或配置时,可以使用原型模式来复制一个已经配置好的对象,从而避免重复的数据准备或配置过程。

4. 当需要动态生成对象的子类时,可以使用原型模式来复制一个已有的对象,并对其进行修改,从而避免重新编写子类的代码。

5. 当需要保护对象的状态时,可以使用原型模式来创建对象的副本,并将副本交给其他对象使用,从而避免原始对象的状态被修改。

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

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

相关文章

【开源】基于Vue.js的生活废品回收系统的设计和实现

目录 一、摘要1.1 项目介绍1.2 项目详细录屏 二、研究内容三、界面展示3.1 登录注册3.2 资源类型&资源品类模块3.3 回收机构模块3.4 资源求购/出售/交易单模块3.5 客服咨询模块 四、免责说明 一、摘要 1.1 项目介绍 生活废品回收系统是可持续发展的解决方案&#xff0c;旨…

云效流水线docker部署 :node.js镜像部署VUE项目

文章目录 引言I 流水线配置1.1 项目dockerfile1.2 Node.js 镜像构建1.3 docker 部署引言 云效流水线配置实现docker 部署微服务项目:https://blog.csdn.net/z929118967/article/details/133687120?spm=1001.2014.3001.5501 配置dockerfile-> 镜像构建->docker部署。 …

【探索Linux】—— 强大的命令行工具 P.14(进程间通信 | 匿名管道 | |进程池 | pipe() 函数 | mkfifo() 函数)

阅读导航 引言一、进程间通信概念二、进程间通信目的三、进程间通信分类四、管道1. 什么是管道2. 匿名管道&#xff08;1&#xff09;创建和关闭⭕pipe() 函数⭕创建匿名管道⭕关闭匿名管道 &#xff08;2&#xff09;通信方式&#xff08;3&#xff09;用法示例&#xff08;4&…

NLP领域的突破催生大模型范式的形成与发展

当前的大模型领域的发展&#xff0c;只是范式转变的开始&#xff0c;基础大模型才刚刚开始改变人工智能系统在世界上的构建和部署方式。 1、大模型范式 1.1 传统思路&#xff08;2019年以前&#xff09; NLP领域历来专注于为具有挑战性的语言任务定义和设计系统&#xff0c…

Leetcode刷题详解—— 目标和

1. 题目链接&#xff1a;494. 目标和 2. 题目描述&#xff1a; 给你一个非负整数数组 nums 和一个整数 target 。 向数组中的每个整数前添加 或 - &#xff0c;然后串联起所有整数&#xff0c;可以构造一个 表达式 &#xff1a; 例如&#xff0c;nums [2, 1] &#xff0c;可…

在GORM中使用并发

一个全面的指南&#xff0c;如何安全地使用GORM和Goroutines进行并发数据处理 效率是现代应用程序开发的基石&#xff0c;而并发在实现效率方面发挥着重要作用。GORM&#xff0c;这个强大的Go对象关系映射库&#xff0c;使开发人员能够通过Goroutines embrace并行性。在本指南…

男科医院服务预约小程序的作用是什么

医院的需求度从来都很高&#xff0c;随着技术发展&#xff0c;不少科目随之衍生出新的医院的&#xff0c;比如男科医院、妇科医院等&#xff0c;这使得目标群体更加精准&#xff0c;同时也赋能用户可以快速享受到服务。 当然相应的男科医院在实际经营中也面临痛点&#xff1a;…

最简WebClient 同步、异步调用示例

目录 一&#xff0c;序言二&#xff0c;简单示例1. 引入依赖2. 日志配置3. 调用代码4. 运行结果 三&#xff0c;完整代码 一&#xff0c;序言 WebClient是Spring WebFlux模块提供的一个非阻塞的基于响应式编程的进行Http请求的客户端工具&#xff0c;从Spring5.0开始WebClient…

C语言——求 n 以内(不包括 n)同时能被 3 和 7 整除的所有自然数之和的平方根 s,n 从键盘输入。

#define _CRT_SECURE_NO_WARNINGS 1#include<stdio.h> #include<math.h> int main() {int i,n;double s0.0;printf("输入任意一个自然数&#xff1a; ");scanf("%d",&n);for(i1;i<n;i) {if(i%30&&i%70){si;}}ssqrt(s);printf(…

软件测试现状以及行业分析

大家都知道最近 ChatGPT 爆火&#xff0c;国外巨头争相宣布自己的相关计划&#xff0c;国内有点实力的企业也在亦步亦趋地跟进。不出意料的是&#xff0c;关于测试职业要被淘汰的话题又&#xff08;为什么要说又&#xff1f;&#xff09;在扎堆出现&#xff0c;内容跟之前还是大…

带有滑动菜单指示器的纯 CSS 导航选项卡

效果展示 CSS 知识点 filter 属性回顾 transition 属性回顾 使用单选框实现导航菜单的思路 单选框当点击完成后就会有一个:checked属性&#xff0c;可以利用这个属性来实现导航菜单底部滑动块的滑动动画和当前菜单项激活状态的管理。 整体页面结构 <div class"tab…

MySQL查询时间处理相关函数与方法实践笔记

1. 实践案例 在查询mysql数据库获取数据时&#xff0c;有这样一个需求&#xff1a;按每30分钟分组获取电量数据&#xff0c;形成1天48个数据点。 方法一&#xff1a; select hour(a.CreateTime) 时点,case when MINUTE(a.CreateTime)<30 then 1 else 2 end 半小时,sum(a…

大数据Doris(二十一):数据导入演示

文章目录 数据导入演示 一、启动zookeeper集群(三台节点都启动) 二、启动hdfs集群

Leetcode -463.岛屿的周长 - 476.数字的补码

Leetcode Leetcode -463.岛屿的周长Leetcode - 476.数字的补码 Leetcode -463.岛屿的周长 题目&#xff1a;给定一个 row x col 的二维网格地图 grid &#xff0c;其中&#xff1a;grid[i][j] 1 表示陆地&#xff0c; grid[i][j] 0 表示水域。 网格中的格子 水平和垂直 方向…

AI:67-基于深度学习的脱机手写汉字识别

🚀 本文选自专栏:AI领域专栏 从基础到实践,深入了解算法、案例和最新趋势。无论你是初学者还是经验丰富的数据科学家,通过案例和项目实践,掌握核心概念和实用技能。每篇案例都包含代码实例,详细讲解供大家学习。 📌📌📌在这个漫长的过程,中途遇到了不少问题,但是…

探索微信小程序框架的精华——高质量的优秀选择

目录 引言&#xff1a; 1. 框架性能 2. 开发者工具支持 3. 文档和社区支持 4. 扩展能力 5. 使用率和稳定性 结语&#xff1a; 引言&#xff1a; 微信小程序作为一种轻量级、高效便捷的应用形式&#xff0c;已经在移动应用领域占据了重要地位。而其中&#xff0c;选择一个…

基于Docker容器DevOps应用方案

文章目录 基于docker容器DevOps应用方案环境基础配置1.所有主机永久关闭防火墙和selinux2.配置yum源3.docker的安装教程 配置主机名与IP地址解析部署gitlab.server主机1.安装gitlab2.配置gitlab3.破解管理员密码4.验证web页面 部署jenkins.server主机1.部署tomcat2.安装jenkins…

【刷题】力扣每日一题 : 381、2300、765

前言 本篇文章用于记录在做力扣每日一题的时候遇到的一些知识点以及自己的思路 381 题干 题目链接 我的思路及做题过程 思路1 我的想法是 记录每个字符串的字母出现个数 然后比较两个字符串是否有字母同时出现 class Solution { public:int judge(string s1, string s2…

001. 变量、环境变量

1、在终端中显示输出 shell脚本通常以shebang起始&#xff1a;#&#xff01;/bin/bash/ shebang是一个文本行&#xff0c;其中#!位于解释器路径之前。/bin/bash是Bash的解释器命令路径。bash将以#符号开头的行视为注释。脚本中只有第一行可以使用shebang来定义解释该脚本所使…

openpnp - 74路西门子飞达控制板(主控板STM32_NUCLEO-144) - 验证

文章目录 openpnp - 74路西门子飞达控制板(主控板STM32_NUCLEO-144) - 验证概述笔记重复数字IO的问题想法手工实现程序实现确定要摘掉的数字重合线自动化测试的问题测试程序的场景测试程序的运行效果测试程序实现备注END openpnp - 74路西门子飞达控制板(主控板STM32_NUCLEO-14…