设计模式(创建型模式)原型模式

目录

    • 一、简介
    • 二、原型模式
      • 2.1、不使用原型模式
      • 2.2、使用原型模式(浅拷贝)
      • 2.3、使用原型模式(深拷贝)
    • 三、原型模式

一、简介

   原型模式(Prototype Pattern) 是一种创建型设计模式,它允许通过复制现有对象来创建新对象,而无需向客户端暴露对象创建的细节。这种模式涉及使用原型实例作为要创建对象的种类,并通过复制这些原型来创建新的对象。

  在原型模式中,客户端通过请求复制(克隆)原型实例来创建一个新的对象,而不是通过标准的对象构造方法。这使得创建新对象更加高效,因为它避免了创建对象的常规创建过程,如实例化和初始化。在实现上,原型模式通常需要目标类实现一个能够克隆自身的接口或方法(例如clone()方法),以便能够通过复制现有对象来生成新对象。

  当涉及到对象的创建时,原型模式允许我们通过复制现有对象来创建新对象。原型模式一般是通过深浅拷贝来实现的。我们假设有个动物类 Animal 和食物类 Food

Animal.java

package com.alian.prototype;import java.io.Serializable;public abstract class Animal implements Serializable {public String name;public String sex;public abstract void sleep();public Animal() {}public Animal(String name, String sex) {this.name = name;this.sex = sex;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getSex() {return sex;}public void setSex(String sex) {this.sex = sex;}
}

Food.java

package com.alian.prototype;import java.io.Serializable;public class Food implements Serializable {private String name;private int num;public Food() {}public Food(String name, int num) {this.name = name;this.num = num;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getNum() {return num;}public void setNum(int num) {this.num = num;}
}

  接下来我们看看没有使用原型模式,和使用原型模式的区别。

二、原型模式

2.1、不使用原型模式

  假设我们有一个狗类 Dog

Dog.java

package com.alian.prototype;import java.io.Serializable;public class Dog extends Animal implements Serializable {private int leg;private String breeds;public Dog() {}public Dog(String name, String sex, int leg, String breeds) {super(name, sex);this.leg = leg;this.breeds = breeds;}public int getLeg() {return leg;}public void setLeg(int leg) {this.leg = leg;}public String getBreeds() {return breeds;}public void setBreeds(String breeds) {this.breeds = breeds;}@Overridepublic void sleep() {System.out.println("睡觉要吹空调");}public static void main(String[] args) {Dog chineseRuralDog = new Dog("大毛", "雄性", 4, "中华田园犬");System.out.println(chineseRuralDog);// 如果需要创建新的用户对象,需要再次使用构造函数创建Dog labrador = new Dog("二毛", "雄性", 4, "拉布拉多");System.out.println(labrador);}
}

  没有使用原型的时候每次都需要 new 一个对象来实现。

2.2、使用原型模式(浅拷贝)

  实现浅拷贝的一个简单方式就是实现了 Cloneable 接口,并重写了 clone() 方法。我们用一个猫类 Cat 来说明浅拷贝。

package com.alian.prototype;import java.io.Serializable;public class Cat extends Animal implements Cloneable, Serializable {private int leg;private String breeds;private Food food;public Cat() {}public Cat(String name, String sex, int leg, String breeds) {super(name, sex);this.leg = leg;this.breeds = breeds;}public int getLeg() {return leg;}public void setLeg(int leg) {this.leg = leg;}public String getBreeds() {return breeds;}public void setBreeds(String breeds) {this.breeds = breeds;}public Food getFood() {return food;}public void setFood(Food food) {this.food = food;}@Overridepublic void sleep() {System.out.println("睡觉要吹空调");}@Overridepublic Cat clone() throws CloneNotSupportedException {return (Cat) super.clone();}public static void main(String[] args) {Cat cat = new Cat("三毛", "雌性", 4, "金吉拉猫");cat.setFood(new Food("鱼", 2));System.out.println("------------------------原对象属性-------------------------");System.out.println("原对象名称:" + cat.getName());System.out.println("原对象性别:" + cat.getSex());System.out.println("原对象脚的数量:" + cat.getLeg());System.out.println("原对象品种:" + cat.getBreeds());System.out.println("原对象口粮:" + cat.getFood().getName());System.out.println("原对象数量:" + cat.getFood().getNum());// 如果需要创建新的用户对象,需要再次使用构造函数创建try {// 使用原型模式创建新的用户对象,通过克隆方法复制现有用户对象Cat newCat = cat.clone();newCat.setName("四毛");newCat.setBreeds("曼基康猫");Food food2 = newCat.getFood();food2.setName("猫粮");food2.setNum(3);System.out.println("------------------------克隆对象属性-------------------------");System.out.println("克隆对象名称:" + newCat.getName());System.out.println("克隆对象性别:" + newCat.getSex());System.out.println("克隆对象脚的数量:" + newCat.getLeg());System.out.println("克隆对象品种:" + newCat.getBreeds());System.out.println("克隆对象口粮:" + newCat.getFood().getName());System.out.println("克隆对象数量:" + newCat.getFood().getNum());System.out.println("------------------------原对象属性-------------------------");System.out.println("原对象名称:" + cat.getName());System.out.println("原对象性别:" + cat.getSex());System.out.println("原对象脚的数量:" + cat.getLeg());System.out.println("原对象品种:" + cat.getBreeds());System.out.println("原对象口粮:" + cat.getFood().getName());System.out.println("原对象数量:" + cat.getFood().getNum());} catch (CloneNotSupportedException e) {e.printStackTrace();}}
}

运行结果:

------------------------原对象属性-------------------------
原对象名称:三毛
原对象性别:雌性
原对象脚的数量:4
原对象品种:金吉拉猫
原对象口粮:鱼
原对象数量:2
------------------------克隆对象属性-------------------------
克隆对象名称:四毛
克隆对象性别:雌性
克隆对象脚的数量:4
克隆对象品种:曼基康猫
克隆对象口粮:猫粮
克隆对象数量:3
------------------------原对象属性-------------------------
原对象名称:三毛
原对象性别:雌性
原对象脚的数量:4
原对象品种:金吉拉猫
原对象口粮:猫粮
原对象数量:3

  通过运行结果我们看到调用 clone() 方法后,然后重新设置了 Food 对象的属性,然后就发现拷贝对象是变了,同时也影响到了原对象的属性 Food (原对象的口粮从 变成 猫粮 ,数量 2 份变成 3 份)。这是为什么呢?

  在Java语言中, clone() 方法执行的是浅拷贝,只会复制对象的基本数据类型(如int,String等,本例中就都没有改变),以及所引用的对象的内存地址,比如我们这里的 Food 的内存地址( 因为改变拷贝对象结果原对象也变了 ),不会递归地复制所引用的对象本身。

2.3、使用原型模式(深拷贝)

  我们还是用上面的示例,不过一定要注意的是深拷贝一般父子类都要实现序列化接口 Serializable 。我们利用字节输入输出流来实现,代码如下:

CloneObject.java

package com.alian.prototype;import java.io.*;public class CloneObject {public static void main(String[] args) throws IOException, ClassNotFoundException {Cat cat = new Cat("三毛", "雌性", 4, "金吉拉猫");cat.setFood(new Food("鱼", 2));System.out.println("------------------------原对象属性-------------------------");System.out.println("原对象名称:" + cat.getName());System.out.println("原对象性别:" + cat.getSex());System.out.println("原对象脚的数量:" + cat.getLeg());System.out.println("原对象品种:" + cat.getBreeds());System.out.println("原对象口粮:" + cat.getFood().getName());System.out.println("原对象数量:" + cat.getFood().getNum());// 如果需要创建新的用户对象,需要再次使用构造函数创建// 使用原型模式创建新的用户对象,通过克隆方法复制现有用户对象Cat newCat = clone(cat);newCat.setName("四毛");newCat.setBreeds("曼基康猫");Food food2 = newCat.getFood();food2.setName("猫粮");food2.setNum(3);System.out.println("------------------------克隆对象属性-------------------------");System.out.println("克隆对象名称:" + newCat.getName());System.out.println("克隆对象性别:" + newCat.getSex());System.out.println("克隆对象脚的数量:" + newCat.getLeg());System.out.println("克隆对象品种:" + newCat.getBreeds());System.out.println("克隆对象口粮:" + newCat.getFood().getName());System.out.println("克隆对象数量:" + newCat.getFood().getNum());System.out.println("------------------------原对象属性-------------------------");System.out.println("原对象名称:" + cat.getName());System.out.println("原对象性别:" + cat.getSex());System.out.println("原对象脚的数量:" + cat.getLeg());System.out.println("原对象品种:" + cat.getBreeds());System.out.println("原对象口粮:" + cat.getFood().getName());System.out.println("原对象数量:" + cat.getFood().getNum());}private static <T extends Serializable> T clone(T obj) throws IOException, ClassNotFoundException {ByteArrayOutputStream baos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(baos);oos.writeObject(obj);ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bais);return (T) ois.readObject();}
}

运行结果:

------------------------原对象属性-------------------------
原对象名称:三毛
原对象性别:雌性
原对象脚的数量:4
原对象品种:金吉拉猫
原对象口粮:鱼
原对象数量:2
------------------------克隆对象属性-------------------------
克隆对象名称:四毛
克隆对象性别:雌性
克隆对象脚的数量:4
克隆对象品种:曼基康猫
克隆对象口粮:猫粮
克隆对象数量:3
------------------------原对象属性-------------------------
原对象名称:三毛
原对象性别:雌性
原对象脚的数量:4
原对象品种:金吉拉猫
原对象口粮:鱼
原对象数量:2

  从结果看到深拷贝对象的属性完美的复制的,并且也没有影响原对象的属性,但是深拷贝明显要比浅拷耗时多,耗内存多。

三、原型模式

  原型模式的优点和缺点如下:

优点:

  • 对象复制: 允许在运行时复制现有对象,无需知道其具体实现。它可以在不影响客户端的情况下生成新对象。
  • 减少资源消耗: 避免了对象的重新创建,提高了性能,特别是当创建对象的成本很高时。
  • 简化对象创建: 通过克隆现有实例来创建新对象,避免了复杂的初始化步骤。适用于对象的初始化操作比较复杂的情况。

缺点:

  • 深拷贝实现困难: 当对象中含有引用类型属性时,需要进行深拷贝才能保证拷贝对象与原始对象的属性完全独立。实现深拷贝可能比较困难。
  • 破坏封装性: 原型模式需要对象实现克隆接口,这可能暴露对象的细节,破坏封装性。
  • 需要理解对象的结构: 使用原型模式需要了解对象的内部结构,因为克隆会直接拷贝对象的字段。
  • 可能产生多个相似对象: 如果拷贝出的对象过多,导致内存消耗增加。需要谨慎使用,避免滥用导致系统资源浪费。

  原型模式适用于需要创建大量相似对象且创建过程复杂的场景,通过复制现有对象来提高效率。但在使用时需要注意深拷贝、克隆方式选择等问题,以避免潜在的性能问题和对象状态的不一致。

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

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

相关文章

百卓Smart管理平台 uploadfile.php 文件上传漏洞(CVE-2024-0939)

免责声明&#xff1a;文章来源互联网收集整理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该…

零基础学Python(9)— 流程控制语句(下)

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。流程控制语句是编程语言中用于控制程序执行流程的语句&#xff0c;本节课就带大家认识下Python语言中常见的流程控制语句&#xff01;~&#x1f308; 目录 &#x1f680;1.while循环 &#x1f680;2.for循环 &#x1…

re:从0开始的CSS学习之路 8. 浏览器默认样式

1. 默认样式 浏览器会有一个默认样式&#xff0c;body标签会有自带一个8px的margin属性。在布局的时候通常不想要浏览器的默认样式&#xff0c;因此可以先清除一下浏览器提供的默认样式 示例如下&#xff1a; <!DOCTYPE html> <html lang"en"><head…

代码随想录算法训练营第41天(动态规划03 ● 343. 整数拆分 ● 96.不同的二叉搜索树

动态规划part03 343. 整数拆分解题思路 96.不同的二叉搜索树解题思路 343. 整数拆分 题目链接&#xff1a; 343. 整数拆分 文章讲解&#xff1a; 343. 整数拆分 视频讲解&#xff1a; 343. 整数拆分 解题思路 动态规划五部曲 确定dp数组以及下标的含义 dp[i]&#xff1a;分…

VUE学习之路序章

前提&#xff1a; 检查是否安装node&#xff0c;要求版本在15及以上。 创建VUE项目 npm init vuelatest 执行完毕之后&#xff0c; cd vue-basenpm installnpm run dev这里可以使用cnpm&#xff0c;cnpm是npm的淘宝镜像&#xff0c;速度较快。 在这里我遇到了一个错误 No matc…

RCE(命令执行)知识点总结最详细

description: 这里是CTF做题时常见的会遇见的RCE的漏洞知识点总结。 如果你觉得写得好并且想看更多web知识的话可以去gitbook.22kaka.fun去看&#xff0c;上面是我写的一本关于web学习的一个gitbook&#xff0c;当然如果你能去我的github为我的这个项目点亮星星我会感激不尽htt…

STM32之定时器

一、简介 STM32F4xx系列共有14个定时器&#xff0c;其中2个高级定时器、10个通用定时器、2个基本定时器。下图 为各定时器及其功能。 图1.各定时器及其功能 二、定时器的计数模式 向上计数模式&#xff1a;计数器从0计数到自动加载值(TIMx_ARR)&#xff0c;然后重新从0开始…

17:定时器编程实战

1、实验目的 (1)使用定时器来完成LED闪烁 (2)原来实现闪烁时中间的延迟是用delay函数实现的&#xff0c;在delay的过程中CPU要一直耗在这里不能去做别的事情。这是之前的缺点 (3)本节用定时器来定一个时间&#xff08;譬如0.3s&#xff09;&#xff0c;在这个定时器定时时间内…

抽象springBoot报错

Failed to configure a DataSource: url attribute is not specified and no embedded datasource could be configured. 中文翻译&#xff1a;无法配置DataSource&#xff1a;未指定“url”属性&#xff0c;并且无法配置嵌入数据源。 DataSource 翻译&#xff1a;数据源 得…

The Back-And-Forth Method (BFM) for Wasserstein Gradient Flows windows安装

本文记录了BFM算法代码在windows上的安装过程。 算法原网站&#xff1a;https://wasserstein-gradient-flows.netlify.app/ github&#xff1a;https://github.com/wonjunee/wgfBFMcodes 文章目录 FFTWwgfBFMcodesMATLABpython注 FFTW 官网/下载路径&#xff1a;https://ww…

警惕钓鱼邮件,保护您的开发者账号

请警惕钓鱼邮件 钓鱼邮件经常冒充官方 Google Play 通信&#xff0c;以窃取敏感信息&#xff0c;并最终为了经济利益盗取开发者账号。 保护开发者免受钓鱼邮件侵害的提示&#xff1a; Google.com 是用于联系开发者的唯一合法电子邮件域名。我们不会通过电子邮件或实时聊天要求您…

【Linux系统学习】 4.Linux实用操作 上

Linux实用操作 1.各类小技巧&#xff08;快捷键&#xff09; 1.1 ctrl c 强制停止 Linux某些程序的运行&#xff0c;如果想要强制停止它&#xff0c;可以使用快捷键ctrl c 命令输入错误&#xff0c;也可以通过快捷键ctrl c&#xff0c;退出当前输入&#xff0c;重新输入 1…

第十六篇【传奇开心果系列】Python的OpenCV库技术点案例示例:图像质量评估

传奇开心果短博文系列 系列短博文目录Python的OpenCV库技术点案例示例短博文系列博文目录前言一、图像质量评估方法和相关函数的介绍二、均方误差示例代码三、峰值信噪比示例代码四、结构相似性指数示例代码五、视频质量评估示例代码六、OpenCV均方根误差计算示例代码七、OpenC…

LLaMA 2 和 QianWen-14B

阿里云通义千问14B模型开源&#xff01;性能超越Llama2等同等尺寸模型 - 科技新闻 - EDA365电子论坛网 LLaMA 2 的硬件要求&#xff1a; LLaMA 2 系列模型有不同的参数量版本&#xff0c;如7B、13B和70B等。对于不同大小的模型&#xff0c;其硬件需求也有所不同。以下是一些硬…

Cilium CNI深度指南

Cilium是基于eBPF的功能强大的CNI插件&#xff0c;为云原生环境提供了强大的网络和安全支持。原文: Cilium CNI: A Comprehensive Deep Dive Guide for Networking and Security Enthusiasts! &#x1f313;简介 欢迎阅读为网络和安全爱好者提供的全面深入的指南&#xff01; 本…

【JavaEE】_传输层协议UDP与TCP

目录 1. 开发中常见的数据组织格式 1.1 XML 1.2 JSON 1.3 Protobuf 2. 端口号 3. UDP协议 4. TCP协议 4.1 特点 4.2 TCP报文格式 4.3 TCP可靠性机制 4.3.1 确认应答机制 4.3.2 超时重传机制 4.3.2.1 丢包的两种情况 4.3.2.2 重传时间 4.3.3 连接管理机制 4.3.3…

VSCode如何让先前打开的文件不被自动关闭,一直保持在标签栏里(关闭预览模式)

第一次接触VSCode-Huawei IDE编辑器&#xff0c;每次打开一个新的代码文件&#xff0c;旧的代码文件都会被自动关闭&#xff08;现在才知道是因为文件默认是以预览模式打开展示的&#xff09;。 那么如何才能让先前打开的文件一直保持在标签栏里呢&#xff1f; 我们需要去设置…

MySQL 升级脚本制作

当数据库更新字段后或添加一些基础信息&#xff0c;要对生产环境进行升级&#xff0c;之前都是手动编写sql&#xff0c;容易出错还容易缺失。 通过 Navcat 工具的数据库结构同步功能和数据同步功能完成数据库脚本的制作。 一、结构同步功能 1、选择 工具–结构同步&#xff1…

【项目技术点总结之三】使用Java生成复杂好看的word或pdf报告的解决方案

前言 项目中往往会遇到需要生成报告的场景&#xff0c;不管是简单报告还是复杂报告&#xff0c;其实都需要找很多资料去尝试&#xff0c;本文会提出几种个人完美解决报告生成的解决方案&#xff0c;而且会提出几个失败但是能生成报告的设想&#xff0c;当然都是踩过坑的&#…

Web3智能合约:重新定义商业合作的未来

随着区块链技术的飞速发展&#xff0c;Web3时代正逐渐到来&#xff0c;而其中的智能合约成为推动商业合作变革的关键力量。本文将深入探讨Web3智能合约的概念、特点以及对商业合作未来的巨大影响。 什么是Web3智能合约&#xff1f; 智能合约是一种以代码形式编写、自动执行合同…