设计模式之创建型模式---原型模式(ProtoType)
文章目录
- 概述
- 类图
- 原型模式优缺点
- 优点
- 缺点
- 代码实现

概述
在有些系统中,往往会存在大量相同或者是相似的对象,比如一个围棋或者象棋程序中的旗子,这些旗子外形都差不多,只是演示或者是上面刻的内容不一样,若此时使用传统的构造函数创建对象,那么简单的对象还好,遇到稍微复杂一点的对象就会耗时耗资源,而使用原型设计模式会让对象的生成高效很多
类图
在本文中使用了人和身体的例子来演示原型模式,假如咱要造一个人的对象,需要设置人的属性,姓名,年龄等,然后给他设置身体,大脑等器官,本例只是为了展示原型模式,只简单的做了Person和Body的结合。
原型模式主要有三个角色
1.抽象原型类:它定义了具体的原型对象必须实现的接口。如本例子中的IProtoType接口
2.具体原型类: 实现抽象原型接口中的复制对象的方法,比如本例中的实现了原型接口中的 clone(),deepClone()方法的Person类
3.访问类: 使用具体原型类中的复制对象方法生成新的对象,比如本例中中的Client
结合本文中的例子,原型设计模式的类图如下所示:
原型模式优缺点
优点
原型设计模式的优点主要有两点,如下所示:
(1)可以优化性能:在JAVA语言中,可以通过实现Cloneable接口,重写clone方法来达到复制对象的目的,这种方式是基于内存二进制流的复制,在性能上比直接使用new关键字创建一个对象高很多。
(2)可以使用原型模式中的深克隆方式保存对象的状态:我们可以使用原型模式将对象复制一份,并将其状态保存起来。简化了创建对象的过程,在需要的时候直接使用我们保存的对象,例如遇到需要恢复到历史某一状态的需求时,或者是需要实现撤销操作的需求时,都可以考虑原型模式
缺点
当然,万事万物有优点就会有缺点,原型模式的缺点主要有三个,如下所示:
(1)需要为每个类都配置一个克隆方法
(2)clone方法位于类的内部,当对已有的类进行修改的时候,需要修改对应的实现代码。不符合开闭原则(对修改关闭,对扩展开放)
(3)当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多层嵌套引用时,每一层对象对应的类都必须支持深克隆,实现起来比较麻烦
深克隆:不仅拷贝对象的本身,而且拷贝对象包含的引用指向的所有对象
浅克隆:仅拷贝对象的本身,而不拷贝对象包含的引用指向的对象
使用一个例子解释深克隆和浅克隆
public class Person {private static final Long VERSION = 1000L;private String name;private int age;private Body body;
// 省略构造函数以及get,set方法
}
比如我们要拷贝上面的Person对象,如果使用深克隆的方式拷贝,这时候Person对象中包含的Body对象也会被拷贝,也就是说,深克隆拷贝出的对象和原来的对象是完全独立的,我们修改新克隆出的对象,不会影响原来的Person对象。假如使用浅克隆,这时只会拷贝Person中包含的Body对象的引用,也就是说使用浅克隆拷贝出来的新对象中包含的Body对象和原来的对象中包含的一样,因为浅克隆将Body的引用拷贝给了新克隆出的对象,这时候如果修改新克隆出的对象,那么原来的Person对象的Body也会跟着变,后面会有例子证实这点
代码实现
本文中,我们使用人和身体的例子来演示原型设计模式。首先我们定义出原型模式的接口,如下所示:
public interface IPrototype extends Cloneable{Person deepClone() throws IOException, ClassNotFoundException;
}
原型模式的接口继承自Java的Cloneable接口,其中包含了一个clone()方法,用于实现浅克隆,而我们定义的接口中
包含了一个deepClone()方法,用于实现深克隆。
然后就是定义一个类实现原型设计模式的接口:
public class Person implements IPrototype, Serializable {private static final Long VERSION = 1000L;private String name;private int age;private Body body;public Person(String name, int age) {this.name = name;this.age = age;}public void setBody(Body body) {this.body = body;}public Body getBody() {return body;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic Person clone() {try {return (Person) super.clone();} catch (CloneNotSupportedException e) {throw new AssertionError();}}public Person deepClone() throws IOException, ClassNotFoundException {// 将对象写入到流中ByteArrayOutputStream byteArrayOutputStream =new ByteArrayOutputStream();ObjectOutputStream outputStream =new ObjectOutputStream(byteArrayOutputStream);outputStream.writeObject(this);// 将对象从流中取出ByteArrayInputStream byteArrayInputStream =new ByteArrayInputStream(byteArrayOutputStream.toByteArray());ObjectInputStream inputStream =new ObjectInputStream(byteArrayInputStream);return (Person) inputStream.readObject();}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +", body=" + body +'}';}
}
需要注意的是,为了实现深克隆,我们需要借助Java的Serializable 接口标识本类可以被序列化。Person类中包含了基本类型的成员变量以及引用类型的成员变量Body,Body的定义如下:
public class Body implements Serializable {private static final Long VERSION = 1001L;private String sex;private String hand;public Body(String sex, String hand) {this.sex = sex;this.hand = hand;}public String getSex() {return sex;}public void setSex(String sex) {this.sex = sex;}public String getHand() {return hand;}public void setHand(String hand) {this.hand = hand;}@Overridepublic String toString() {return "Body{" +"sex='" + sex + '\'' +", hand='" + hand + '\'' +'}';}
}
为了能实现序列化,Body类也要实现Serializable接口。当需要使用浅克隆的时候,我们就通过Person对象的clone()方法来生成,,当需要使用深克隆的时候,我们就使用deepClone()方法来生成。
最后就是使用对应的克隆方法生成克隆对象
public class Client {public static void main(String[] args) throws IOException, ClassNotFoundException {// 创建一个Person对象Person person = new Person("walt", 18);// 创建出Body对象Body body = new Body("男", "男生的手");person.setBody(body);System.out.println("原始的Person: " + person);// 使用浅克隆生成一个克隆对象Person clonePerson = person.clone();System.out.println("克隆的Person: " + clonePerson);// 获取到克隆对象的Body并做修改Body cloneBody = clonePerson.getBody();cloneBody.setSex("女");cloneBody.setHand("女生的手");clonePerson.setBody(cloneBody);// 分别打印出克隆的对象和原始对象System.out.println("克隆Person: " + clonePerson + " ,原始Person: " + person);// 检查克隆对象的body和原始对象的body是否是同一个,是就为浅克隆,不是为深克隆System.out.println("克隆的Body是否等于原始的Body: " + (body == cloneBody));// 使用深克隆生成一个对象Person deepClonePerson = person.deepClone();// 获取到深克隆后的person对象的body并修改Body deepCloneBody = deepClonePerson.getBody();deepCloneBody.setSex("深克隆男孩子");deepCloneBody.setHand("深克隆男生的手手");deepClonePerson.setBody(deepCloneBody);// 打印出深克隆后的对象和原始的对象System.out.println("深克隆Person: " + deepClonePerson + " ,原始Person: " + person);// 检查克隆对象的body和原始对象的body是否是同一个,是就为浅克隆,不是为深克隆System.out.println("深克隆的Body是否等于原始的Body: " + (body == deepCloneBody));}
}
运行结果:
相关文章:

设计模式之创建型模式---原型模式(ProtoType)
文章目录 概述类图原型模式优缺点优点缺点 代码实现 概述 在有些系统中,往往会存在大量相同或者是相似的对象,比如一个围棋或者象棋程序中的旗子,这些旗子外形都差不多,只是演示或者是上面刻的内容不一样,若此时使用传…...

git命令新建远程仓库
今天记录一下使用git命令新建远程分支的操作,因为公司的代码管理仓库界面没找到新建分支的操作界面,无奈只能通过git命令来新建分支。 1、新建本地分支 首先,你的至少应该已经有了一个master分支,然后你再master分支下面执行下面…...

Defog发布Llama-3-SQLCoder-8B,文本转SQL模型,性能比肩GPT-4,准确率超90%,消费级硬件可运行
前言 在计算语言学领域,将自然语言转化为可执行的SQL查询是一个重要的研究方向。这对于让那些没有编程或SQL语法知识的用户也能轻松访问数据库信息至关重要。Defog团队近日发布了基于Llama-3的SQLCoder-8B模型,它在文本转SQL模型领域取得了显著突破&…...

防刷发送短信验证码接口的五种简单好用方法绝对够用
防刷发送短信验证码接口的五种简单好用方法,绝对够用 前端增加图形验证码,点击发送按钮后增加60s倒计时,60s后才可以再次点击 后端对接口次数校验,60s内同一电话号码只能发送一次 // 生成基于电话号码的重试锁定键 String repeat…...

ubuntu中idea创建spark项目步骤
1.前置条件 ubuntu中已经安装idea,jdk,scala,spark 2.打开idea,新建,选择Maven项目 3.在IDEA中,File-Setting-Plugin,下载Scala插件 4.File-project structure,导入插件 4.1在全局库中,选择导入刚才的sca…...

回文链表(快慢指针解法之在推进过程中反转)
归纳编程学习的感悟, 记录奋斗路上的点滴, 希望能帮到一样刻苦的你! 如有不足欢迎指正! 共同学习交流! 🌎欢迎各位→点赞 👍 收藏⭐ 留言📝抱怨深处黑暗,不如提灯前行…...

深度剖析:为什么 Spring 和 IDEA 都不推荐使用 @Autowired 注解
目录 依赖注入简介 Autowired 注解的优缺点 Spring 和 IDEA 不推荐使用 Autowired 的原因 构造器注入的优势 Autowired 注解的局限性 可读性和可测试性的问题 推荐的替代方案 构造器注入 Setter 注入 Java Config Bean 注解 项目示例:Autowired vs 构造器…...

【接口自动化_05课_Pytest接口自动化简单封装与Logging应用】
一、关键字驱动--设计框架的常用的思路 封装的作用:在编程中,封装一个方法(函数)主要有以下几个作用:1. **代码重用**:通过封装重复使用的代码到一个方法中,你可以在多个地方调用这个方法而不是…...

信息学奥赛初赛天天练-14-阅读程序-字符数组、唯一分解定理应用
更多资源请关注纽扣编程微信公众号 1 2019 CSP-J 阅读程序1 (程序输入不超过数组或字符串定义的范围;判断题正确填√,错误填;除特殊说明外,判断题1.5分,选择题3分,共计40分) 1 输入的字符串只能由小写字母或大写字母组…...

K210 数字识别 笔记
一、烧写固件 连接k210开发板,点开烧录固件工具,选中固件,并下载 二、模型训练 网站:MaixHub 1、上传文件 2、开始标记数据 添加9个标签,命名为1~9,按键盘w开始标记,键盘D可以下一张图片&…...

人脸检测--FaceNet(四)
FaceNet 是一个由 Google 研究团队开发的人脸识别系统,它基于深度学习技术,可以实现高精度的人脸识别、验证和聚类任务。FaceNet 通过学习直接从图像像素到人脸嵌入的映射,使得它在各种人脸识别任务中表现出色。下面是对 FaceNet 的详细介绍&…...

Android性能优化方案
1.启动优化: application中不要做大量耗时操作,如果必须的话,建议异步做耗时操作2.布局优化:使用合理的控件选择,少嵌套。(合理使用include,merge,viewStub等使用)3.apk优化(资源文件优化&#…...

视频监控平台AS-V1000 的场景管理,一键查看多画面视频的场景配置、调用、管理(一键浏览多路视频)
目录 一、场景管理的定义 二、场景管理的功能和特点 1、功能 (1)场景配置 (2)实时监控 (3)权限管理 2、特点 三、AS-V1000的场景配置和调用 1、场景配置 (1)实时视频预览 …...

微服务架构五大设计模式详解,助你领跑行业
微服务架构设计模式详解(5种主流模式) 微服务架构 微服务,一种革命性的架构模式,主张将大型应用分解为若干小服务,通过轻量级通信机制互联。每个服务专注特定业务,具备独立部署能力,轻松融入生产环境,为系…...

【problem】解决EasyExcel导出日期数据显示为#####问题
前言 在使用EasyExcel进行数据导出时,你可能遇到日期或其他数据在Excel中显示为“#######”的情况,这通常是因为列宽不足以展示单元格内的全部内容。本文将指导你如何通过简单的步骤解决这一问题,并确保导出的Excel文件自动调整列宽或直接指…...

Pytest用例自定义 - 重复、并行、串行
简介:面对快速迭代和持续交付的需求,提高测试效率变得至关重要。并行测试因其显著的时间节省优势而备受青睐。然而,并非所有测试都适合并行执行。在某些情况下,串行执行是必要的,以确保测试的正确性和稳定性。本文将探…...

前端项目上线
目录 1项目打包 2本地服务器部署 2.1具体操作步骤 2.2解决刷新 404 问题 2.3请求无法发送问题 3nginx 服务器部署 3.2nginx 配置代理练习 安装nginx nginx部署启动项目 3.3nginx 部署前端项目 4云服务器部署 本地资源上传 配置服务器与nginx 1项目打包 ●我…...

redis基本数据结构与应用
文章目录 概要String结构Hash结构List结构Set结构Zset结构bitmap位图类型geo地理位置类型其他常用命令 概要 redis常用的5种不同数据结构类型之间的映射如下: 结构类型结构存储的值结构的读写能力STRING可以是字符串、整数或者浮点数key-value形式;对整…...

Python pands使用引擎实现excel条件格式
截至我的知识更新日期(2023年),Pandas 库本身并不直接支持Excel条件格式。Pandas 是一个强大的Python数据分析库,它主要用于数据分析和操作,而不是用于创建或编辑Excel文件的格式。 然而,你可以使用 openp…...

基于 vuestic-ui 实战教程 - 登录篇
1. 简介 登录做为一个系统的门面,也是阻挡外界的一道防线,那在vuestic-ui中如何做登录功能呢。在这里就之间沿用初始版本的Login页面,作为一个演示模板,后续需要改进的读者可以在此篇文章的基础上修改。 2. 登录接口相关api 与 t…...

SAPUI5基础知识2 - 手动创建一个SAPUI5的项目
1. 前言 在本篇文章中,我们将手动一步一步建立出第一个SAPUI5的 ‘Hello World!’ 项目。 2. 步骤详解 2.1 在BAS中建立Dev Space 进入SAP Business Application Studio的Dev Space Manger,选择创建Dev Space。 勾选HTML5 Application Template插件…...

设计模式--访问者模式
访问者模式是一种行为设计模式,它用于将算法与对象结构分离,使得算法可以独立于使用它的数据结构而变化。这种模式在许多应用场景中非常有用,例如在实现图形算法、数据结构遍历、文件格式转换以及代码分析时。 应用场景 图形算法࿱…...

onnx模型转换到rknn脚本
from rknn.api import RKNN ONNX_MODEL ./onnx_models/yolov5s_rm_transpose.onnx # platform"rk1808" platform "rv1109" RKNN_MODEL yolov5s_relu_{}_out_opt.rknn.format(platform) if __name__ __main__: add_perm False # 如果设置成True,则将模…...

防御恶意爬虫攻击
数据抓取爬虫 数据抓取爬虫是攻击者使用自动化脚本或工具在移动应用程序中抓取敏感数据的一种方式。这些爬虫可以定向抓取用户信息、产品列表、评论和评级等数据。攻击者可能会将这些数据用于非法目的,例如进行身份盗窃、诈骗活动或者卖给其他恶意方。 对于移动应用…...

【自动驾驶技术栈学习】2-软件《大话自动驾驶》| 综述要点总结 by.Akaxi
----------------------------------------------------------------------------------------------------------------- 致谢:感谢十一号线人老师的《大话自动驾驶》书籍,收获颇丰 链接:大话自动驾驶 (豆瓣) (douban.com) -------------…...

SRS视频服务器应用研究
1.SRS尝试从源码编译启动 1.1.安装ubuntu 下载镜像文件 使用VMWare安装,过程中出现蓝屏,后将VM的软件版本从15.5升级到17,就正常了。 1.2.更新ubuntu依赖...

没有括号的字符串四则运算
目录 问题分析与解答evalsympy消去法逆波兰表达式拓展思考参考资料 问题 用代码实现一个method,这个method的入参是一个字符串,这个字符串是一个四则运算的算式,比如“12*34/2-3”;返回值是这个算式的运算结果,比如“…...

vue2 $set 后期添加响应式数据的问题,使用vm.$set()
文章目录 后期添加数据的问题后期给Vue的实例添加的属性,会有响应式吗?避免在运行时向vm或其根$data添加响应式 对象的响应式处理想给后期追加的属性添加响应式处理的,有以下俩个方法: 数组的响应式处理解决方案一:解决…...

笔记-X86下用Docker运行ARM64编译Libreoffice
初衷 针对恶略环境下的自适应,记个笔记,苦于没有外网的arm架构环境,内网中安装个arm类型的deb,难如登天,突然发现这个好东西。 参考引用 x86架构的Ubuntu上通过Docker运行ARM架构的系统 前提 docker已经安装好 安…...

力扣:92. 反转链表 II(Java)
目录 题目描述:示例 1:示例 2:代码实现: 题目描述: 给你单链表的头指针 head 和两个整数 left 和 right ,其中 left < right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的…...