【设计模式-2.3】创建型——原型模式
说明:本文介绍设计模式中,创建型中的原型模式;
飞机大战
创建型设计模式关注于对象的创建,原型模式也不例外。如简单工厂和工厂模式中提到过的飞机大战这个例子,游戏中飞机、坦克对象会创建许许多多的实例,每个实例除了坐标,是一模一样的,如果每次都用关键字new去创建,是非常消耗时间的。

(Enemy,敌人抽象类)
/*** 敌人抽象类*/
public abstract class Enemy {/*** 敌人的坐标*/protected int x;/*** 敌人的坐标*/protected int y;/*** 抽象方法*/public Enemy(int x, int y) {this.x = x;this.y = y;}/*** 绘制方法*/public abstract void show();
}
(AirPlane,飞机类)
/*** 飞机*/
public class AirPlane extends Enemy {public AirPlane(int x, int y) {super(x, y);}@Overridepublic void show() {System.out.println("飞机出现了,坐标是:" + x + "," + y);}
}
(Client,客户端类,循环创建对象,浪费资源,影响效率)
import java.util.ArrayList;
import java.util.List;
import java.util.Random;/*** 客户端*/
public class Client {public static void main(String[] args) {// 屏幕宽度int screenLength = 100;// 定义飞机集合List<AirPlane> airPlanes = new ArrayList<>();// 创建飞机for (int i = 0; i < 100000; i++) {airPlanes.add(new AirPlane(new Random().nextInt(screenLength), 0));}}
}
原型模式
原型模式,是使用了Object类里面的clone()方法,对原型对象,也就是创建的第一个对象进行了克隆,避免重复创建对象。如下:
(AirPlane,飞机类,实现Cloneable接口)
/*** 飞机*/
public class AirPlane extends Enemy implements Cloneable {public AirPlane(int x, int y) {super(x, y);}@Overridepublic void show() {System.out.println("飞机出现了,坐标是:" + x + "," + y);}/*** clone方法,调用父类的clone方法*/public AirPlane clone() {Object obj = null;try {obj = super.clone();return (AirPlane) obj;} catch (CloneNotSupportedException e) {System.out.println("克隆失败");return null;}}
}
(AirPlaneFactory,飞机工厂类,调用飞机的克隆方法)
/*** 飞机工厂*/
public class AirPlaneFactory {/*** 加载一个原型对象*/private static AirPlane airPlane = new AirPlane(0, 0);/*** 获取飞机* @param x* @return*/public static AirPlane getAirPlane(int x) {AirPlane clone = airPlane.clone();clone.setX(x);return clone;}
}
(客户端类,创建飞机实例对象)
import java.util.ArrayList;
import java.util.List;
import java.util.Random;/*** 客户端*/
public class Client {public static void main(String[] args) {// 屏幕宽度int screenLength = 100;// 定义飞机集合List<AirPlane> airPlanes = new ArrayList<>();// 创建飞机for (int i = 0; i < 100000; i++) {airPlanes.add(AirPlaneFactory.getAirPlane(new Random().nextInt(screenLength)));}// 比较两个飞机是否相等System.out.println(airPlanes.get(0) == airPlanes.get(1));}
}
克隆的实例,是不相等的;

以上就是通过原型模式实例化对象的过程,可以节约内存空间。
浅拷贝和深拷贝
在使用了clone()方法时,我们需要知道浅拷贝和深拷贝的概念。在Java中有基本数据类型和引用数据类型,其中基本数据类型有8种,分别是byte、short、int、long、float、double、char、boolean,除此之外的都是引用数据类型。
对于基本数据类型,浅拷贝和深拷贝都是创建一个新对象,而对于引用数据类型,浅拷贝是拷贝一个对象的指针,深拷贝是拷贝一个一模一样的对象。
例如,在上面的基础上,我创建一个机长类,在飞机类里面注入一个机长类对象,克隆之后,对飞机里面的机长对象进行判断,如下:
(机长类)
/*** 机长*/
public class Captain implements Cloneable{/*** 姓名*/private String name;/*** 年龄*/private Integer age;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 class AirPlane extends Enemy implements Cloneable {/*** 机长*/private Captain captain;public AirPlane(int x, int y, Captain captain) {super(x, y);this.captain = captain;}@Overridepublic void show() {System.out.println("飞机出现了,坐标是:" + x + "," + y);}/*** clone方法,调用父类的clone方法*/public AirPlane clone() {Object obj = null;try {obj = super.clone();return (AirPlane) obj;} catch (CloneNotSupportedException e) {System.out.println("克隆失败");return null;}}public Captain getCaptain() {return captain;}
}
(克隆对象后,对飞机对象和飞机对象里面的机长对象进行判断)
// 比较两个飞机是否相等System.out.println(airPlanes.get(0) == airPlanes.get(1));// 比较两个飞机的机长是否相等System.out.println(airPlanes.get(0).getCaptain() == airPlanes.get(1).getCaptain());
结果是飞机对象是互不相同的,但是飞机对象中的引用数据类型,是相同的,是浅拷贝;

这肯定是不行的,每个实例的数据应该是封装独有的,不能“克隆了,但没完全克隆”。我们可以采用字节流的方式实现“深拷贝”,如下:
(敌人抽象类)
/*** 敌人抽象类*/
public abstract class Enemy {/*** 敌人的坐标*/protected int x;/*** 敌人的坐标*/protected int y;public int getX() {return x;}public void setX(int x) {this.x = x;}public int getY() {return y;}/*** 绘制方法*/public abstract void show();
}
(飞机类,使用IO流的方式来实现深拷贝)
import java.io.*;/*** 飞机*/
public class AirPlane extends Enemy implements Serializable {/*** 机长*/private Captain captain;public Captain getCaptain() {return captain;}public void setCaptain(Captain captain) {this.captain = captain;}@Overridepublic void show() {System.out.println("飞机出现了,坐标是:" + x + "," + y);}/*** 获取飞机* @param x* @return*/public AirPlane getAirPlane(int x) throws IOException, ClassNotFoundException {this.setX(x);// 创建对象输出流ByteArrayOutputStream bao = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bao);oos.writeObject(this);// 返回克隆对象ByteArrayInputStream bis = new ByteArrayInputStream(bao.toByteArray());ObjectInputStream ois = new ObjectInputStream(bis);return (AirPlane) ois.readObject();}
}
(机长类)
import java.io.Serializable;/*** 机长*/
public class Captain implements Serializable {/*** 姓名*/private String name;/*** 年龄*/private Integer age;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;}
}
创建对象,调用“克隆”方法,比较两个对象的地址和对象内的引用类型数据的地址;
import java.io.IOException;
import java.util.Random;/*** 客户端*/
public class Client {public static void main(String[] args) throws IOException, ClassNotFoundException {// 屏幕宽度int screenLength = 100;AirPlane airPlane = new AirPlane();airPlane.setCaptain(new Captain());AirPlane airPlaneNew = airPlane.getAirPlane(new Random().nextInt(screenLength));// 比较两个飞机是否相等System.out.println(airPlane == airPlaneNew);// 比较两个飞机的机长是否相等System.out.println(airPlane.getCaptain() == airPlaneNew.getCaptain());}
}
都为false,说明“深拷贝”已经实现。

深克隆的实现并未用到Object的clone方法,而是使用了IO流中的Object流的方式,对对象进行序列化来实现的。
原型管理器
原型管理器的构想是,创建一个管理器类,包含了所有需要克隆的对象,需要的时候可以直接调用管理器中的方法,取一个克隆的对象出来。代码如下:
(定义一个敌人接口,一个克隆方法,一个展示方法)
/*** 敌人接口*/
public interface Enemy extends Cloneable{/*** 克隆方法* @return*/Enemy clone();/*** 显示方法*/void show();
}
(分别定义敌人飞机、敌人坦克对象)
/*** 敌人飞机*/
public class AirPlane implements Enemy{@Overridepublic Enemy clone() {AirPlane airPlane = null;try {airPlane = (AirPlane) super.clone();} catch (CloneNotSupportedException e) {System.out.println("克隆失败");}return airPlane;}@Overridepublic void show() {System.out.println("敌人飞机");}
}/*** 敌人坦克*/
public class Tank implements Enemy{@Overridepublic Enemy clone() {Tank tank = null;try {tank = (Tank) super.clone();} catch (CloneNotSupportedException e) {System.out.println("克隆失败");}return tank;}@Overridepublic void show() {System.out.println("敌人坦克");}
}
(定义原型管理器,通过getKey()的方式获取到对应对象的克隆实例)
import java.util.Hashtable;/*** 原型管理类*/
public class PrototypeManagement {private Hashtable ht = new Hashtable();private static PrototypeManagement pm = new PrototypeManagement();/*** 添加敌人* @param key* @param enemy*/public void addEnemy(String key, Enemy enemy) {ht.put(key, enemy);}/*** 获取敌人* @param key* @return*/public Enemy getEnemy(String key) {Enemy enemy = (Enemy) ht.get(key);return enemy.clone();}public static PrototypeManagement getPrototypeManagement() {return pm;}
}
(客户端测试)
/*** 原型管理器客户端*/
public class Client {public static void main(String[] args) {PrototypeManagement pm = PrototypeManagement.getPrototypeManagement();// 添加敌人pm.addEnemy("airplane", new AirPlane());pm.addEnemy("tank", new Tank());AirPlane airplane = (AirPlane) pm.getEnemy("airplane");airplane.show();AirPlane airplaneNew = (AirPlane) pm.getEnemy("airplane");airplaneNew.show();System.out.println(airplane == airplaneNew);System.out.println("====================================");Tank tank = (Tank) pm.getEnemy("tank");tank.show();Tank tankNew = (Tank) pm.getEnemy("tank");tankNew.show();System.out.println(tank == tankNew);}
}
通过克隆获取实例,地址不相等;

此时,如果需要增加一个Boss,非常简单,只需要新增一个类实现Enemy接口即可;
总结
本文参考《设计模式的艺术》、《秒懂设计模式》两书
相关文章:
【设计模式-2.3】创建型——原型模式
说明:本文介绍设计模式中,创建型中的原型模式; 飞机大战 创建型设计模式关注于对象的创建,原型模式也不例外。如简单工厂和工厂模式中提到过的飞机大战这个例子,游戏中飞机、坦克对象会创建许许多多的实例࿰…...
八大插入算法(有注释)
直接插入排序 //直接插入排序 void InsertSortingDirectly(int* nums,int numsSize){int j0;for(int i1;i<numsSize-1;i){//定义一个中间变量保存当前要插入的值int tempnums[i];//在前面已排好序的序列中,找到合适的位置插入for(ji-1;j>0;j--){if(nums[j]&g…...
【2】基于多设计模式下的同步异步日志系统
6. 相关技术知识补充 6.1 不定参函数 在初学C语⾔的时候,我们都⽤过printf函数进⾏打印。其中printf函数就是⼀个不定参函数,在函数内部可以根据格式化字符串中格式化字符分别获取不同的参数进⾏数据的格式化。 ⽽这种不定参函数在实际的使⽤中也⾮常…...
npm管理发布包-创建与发布
创建与发布 我们可以将自己开发的工具包发布到 npm 服务上,方便自己和其他开发者使用,操作步骤如下 创建文件夹,并创建文件indexjs,在文件中声明函数,使用 module.exports 暴露npm初始化工具包,package.j…...
基于Spring,SpringMVC,MyBatis的校园二手交易网站
文章目录 项目介绍主要功能截图:部分代码展示设计总结项目获取方式🍅 作者主页:超级无敌暴龙战士塔塔开 🍅 简介:Java领域优质创作者🏆、 简历模板、学习资料、面试题库【关注我,都给你】 🍅文末获取源码联系🍅 项目介绍 基于Spring,SpringMVC,MyBatis的校园二…...
酒店 KPI绩效考核指标及应用
“路遥知马力,日久见人心”,目前国内各类型酒店风起云涌,大有在市场竞争中一比高下之势,各路精英受经济型酒店低投入高回报的市场利益驱动,都分分抢占市场,从而使国内经济型酒店的数量不断增加,…...
WordPress两种方法实现上传媒体图片文件自动重命名
我们发布文章时,会上传一些图片、音频之类的文件。但是WordPress没有自动 给新上传文件重命名的功能,逐个文件去重命名那就太麻烦了,那么我们改如何自动给上传的媒体文件图片重命名呢? 我在网站搜索了些上WordPress上传媒体文件自…...
TZOJ 1405 An easy problem
翻译有些出错,但大概是那个意思 答案: #include <stdio.h> #include <ctype.h> //引用库函数isupper的头文件int main() {int T 0, i 0;scanf("%d", &T); //要输入的行数while (T--) //循环T次{char c;int y 0…...
SpringBoot+mysql+vue实现大学生健康档案管理系统前后端分离
一、项目简介 本项目是一套基于SpringBoot实现大学生健康档案管理系统,主要针对计算机相关专业的正在做bishe的学生和需要项目实战练习的Java学习者。 包含:项目源码、数据库脚本等,该项目可以直接作为bishe使用。 项目都经过严格调试&#…...
CCC联盟数字车钥匙(三)——UWB MAC时间网格同步及Hopping
本文继续上一篇UWB MAC时间网格继续介绍UWB MAC中关于时间同步相关内容。 3、MAC时间网格同步 每个测距会话的定义都基于相对的指定时钟参考 U W B t i m e 0 k UWB^k_{time0} UWBtime0k,相对于发起者的内部时钟定义。 时钟参考 U W B t i m e 0 k UWB^k_{time0} …...
一周上手 steam搬砖项目或成2024年最受欢迎副业
蒸汽砖拆除项目,兼职创业两不误,助你轻松赚钱 你是否想要找到一个既可以兼职又可以创业的项目?蒸汽砖拆除项目正逐渐崭露头角,引起了越来越多人的关注。这个项目不仅门槛低,上手快,而且不用担心卖不出去&am…...
java数据结构(哈希表—HashMap)含LeetCode例题讲解
目录 1、HashMap的基本方法 1.1、基础方法(增删改查) 1.2、其他方法 2、HashMap的相关例题 2.1、题目介绍 2.2、解题 2.2.1、解题思路 2.2.2、解题图解 2.3、解题代码 1、HashMap的基本方法 HashMap 是一个散列表,它存储的内容是键…...
快速了解ChatGPT(大语言模型)
目录 GPT原理:文字接龙,输入一个字,后面会接最有可能出现的文字。 GPT4 学会提问:发挥语言模型的最大能力 参考李宏毅老师的课快速了解大语言模型做的笔记: Lee老师幽默的开场: GPT:chat Ge…...
计算机软件的分类
以功能进行分类,计算机软件通常可以分为系统软件和应用软件两大类。 系统软件:系统软件是计算机运行和管理的基本软件,包括操作系统、驱动程序、系统工具和服务程序等。操作系统是系统软件的核心,负责管理计算机的硬件资源、提供用…...
数据库应用:Ubuntu 20.04 安装MongoDB
目录 一、理论 1.MongoDB 二、实验 1.Ubuntu 20.04 安装MongoDB 三、问题 1.Ubuntu Linux的apt 包管理器更新安装软件报错 2.Ubuntu20.04安装vim报错 3.Ubuntu20.04如何更换阿里源 4.Ubuntu22.04如何更换阿里源 一、理论 1.MongoDB (1)概念 …...
服务器配置 jupyter lab,并在本地浏览器免密登陆
一、背景 快速搭建一个jupyter lab 不用每次用ssh登录输入密码 二、步骤 方法1、临时在服务器启动 jupyter lab,并在本地浏览器免密登陆 两句命令解决 pip install jupyterlabnohup jupyter lab --ServerApp.ip"*" --ServerApp.password"" -…...
WebUI自动化学习(Selenium+Python+Pytest框架)002
新建项目 New Project 新建一个python代码文件 file-new-python file 会自动创建一个.py后缀的代码文件 注意:命名规则,包含字母、数字、下划线,不能以数字开头,不能跟python关键字或包名重复。 ********************华丽分割线********************…...
miot-plugin-sdk. npm install安装失败
miot-plugin-sdk-npm install安装失败 最紧公司要开发一台智能设备,经过同事的对比,选中了米家作为云平台,于是,我就负责开发app界面端,根据官方文档教程 下载了miot-plugin-sdk 程序,准备开始开发,结果悲…...
抓取微信好友列表信息
本文实现的是一种较为安全、简洁、高效的抓取微信好友信息的方法。 实现工具:微信pc端、影刀RPA 主要流程: 手动—前期准备,电脑登陆微信,打开联系人页,使得联系人分类“A”显现在微信窗口界面 自动—运行程序&#…...
创建JDK8版本的SpringBoot项目的方法
目录 一.通过阿里云下载 二.通过IDEA创建 1.下载安装JDK17 2.创建SpringBoot 3.X的项目 3.把JDK17改成JDK8 截止到2023.11.24,SpringBoot不再支持3.0X之前的版本,3.0X之后的版本所对应的JDK版本为JDK17,下面介绍如何在idea上继续使用JDK…...
雷小兔:让学术论文排版变得简单高效
产品概述 雷小兔是一款专门为学生和研究人员设计的学术论文辅助工具。无论你是在准备毕业论文、学位论文还是学术发表,雷小兔都能为你提供全面的支持和帮助。 论文排版方面的核心优势 1. 模板齐全,开箱即用 雷小兔内置了数十种符合国内外高校标准的论…...
青铜器RDM研发管理平台
我们深耕研发管理服务20余年,依托 10 余年研发管理实战经验,累计为超 10000 家企业提供专业培训、为200 余家企业深度咨询,打造完全自主知识产权的研发管理数字化平台 —— 青铜器 RDM。以 IPD、CMMI、Scrum、PMBOK 等业界最佳实践为内核&…...
实现网页完整捕获:Full Page Screen Capture技术解析与应用指南
实现网页完整捕获:Full Page Screen Capture技术解析与应用指南 【免费下载链接】full-page-screen-capture-chrome-extension One-click full page screen captures in Google Chrome 项目地址: https://gitcode.com/gh_mirrors/fu/full-page-screen-capture-chr…...
KOReader终极指南:如何打造你的完美电子墨水屏阅读体验
KOReader终极指南:如何打造你的完美电子墨水屏阅读体验 【免费下载链接】koreader An ebook reader application supporting PDF, DjVu, EPUB, FB2 and many more formats, running on Cervantes, Kindle, Kobo, PocketBook and Android devices 项目地址: https:…...
别再混淆了!一文讲透NvDecoder里ulNumDecodeSurfaces和ulNumOutputSurfaces到底怎么用
深入解析NvDecoder:解码缓存与输出缓存的本质区别与实战配置 在视频处理领域,NVIDIA的硬件解码器(NVDEC)因其出色的性能和高效的资源利用率而广受开发者青睐。然而,对于许多中高级开发者来说,NvDecoder中ul…...
别再为PyTorch GPU环境发愁了!手把手教你用Miniconda管理多版本CUDA(GTX1060实测)
深度学习环境配置实战:GTX1060显卡下的PyTorch GPU环境搭建指南 在深度学习领域,环境配置往往是新手面临的第一个挑战。特别是当您手头有一块GTX1060这样的经典显卡时,如何充分发挥其计算潜力,同时避免陷入版本兼容性问题的泥潭&…...
告别‘夜盲症’:用Python+OpenCV手把手教你实现红外与可见光图像融合(附完整代码)
实战指南:PythonOpenCV实现红外与可见光图像融合技术 夜间监控画面总是模糊不清?自动驾驶系统在低光照环境下识别率骤降?这些问题本质上都是"视觉夜盲症"的表现。今天我们将用最实用的方式,带你用Python和OpenCV构建一个…...
揭秘Captum归因算法:5种NLP文本分类与情感分析的最佳实践
揭秘Captum归因算法:5种NLP文本分类与情感分析的最佳实践 【免费下载链接】captum Model interpretability and understanding for PyTorch 项目地址: https://gitcode.com/gh_mirrors/ca/captum 在当今人工智能快速发展的时代,模型可解释性已成为…...
cool-admin(midway版)数据库事务超时:超时设置与回滚机制终极指南
cool-admin(midway版)数据库事务超时:超时设置与回滚机制终极指南 【免费下载链接】cool-admin-midway 🔥 cool-admin(midway版)一个很酷的后台权限管理框架,模块化、插件化、CRUD极速开发,永久开源免费,基于midway.js…...
快马ai一键生成:windows 11自动化部署openclaw环境原型脚本
最近在折腾Windows 11的开发环境配置,发现每次换新机器都要重复安装一堆工具链特别麻烦。正好发现了OpenClaw这个开源工具,它号称能自动化搞定开发环境部署。不过手动安装配置还是有点繁琐,于是我用InsCode(快马)平台快速生成了一个自动化安装…...
