【设计模式-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…...
Beyond Compare许可证获取与激活全攻略
1. Beyond Compare简介与许可证类型解析 Beyond Compare作为一款老牌文件对比工具,已经陪伴开发者走过了20多个年头。我第一次接触它是在2015年做代码合并时,当时就被它直观的三栏式对比界面惊艳到了——左右两侧显示对比内容,中间实时标注差…...
小红书数据采集实战:5个Python技巧让爬虫更智能
小红书数据采集实战:5个Python技巧让爬虫更智能 【免费下载链接】xhs 基于小红书 Web 端进行的请求封装。https://reajason.github.io/xhs/ 项目地址: https://gitcode.com/gh_mirrors/xh/xhs 在小红书这个拥有数亿用户的社交电商平台上,海量的用…...
GTE-Base-ZH一键部署教程:3步在Ubuntu上搭建语义检索服务
GTE-Base-ZH一键部署教程:3步在Ubuntu上搭建语义检索服务 想给自己的应用加个智能搜索功能,但一看到复杂的模型部署就头疼?别担心,今天咱们就来聊聊怎么用最简单的方法,在Ubuntu系统上把GTE-Base-ZH这个强大的中文语义…...
SAP FI模块实战:OBC4配置字段状态变式全流程解析(含常见报错处理)
SAP FI模块深度实战:OBC4字段状态变式配置与冲突解决指南 1. 字段状态变式的核心价值与应用场景 在SAP财务模块中,字段状态变式(Field Status Variants)是控制会计凭证输入界面的关键配置项。它决定了用户在创建财务凭证时&#x…...
Nunchaku-flux-1-dev模型服务监控:使用Node.js搭建性能仪表盘
Nunchaku-flux-1-dev模型服务监控:使用Node.js搭建性能仪表盘 你是不是也遇到过这种情况?自己部署的AI模型服务,用着用着突然就变慢了,或者干脆没响应了,用户反馈过来才知道出了问题。等到发现的时候,可能…...
从Simulink到实物:单闭环直流调速仿真如何指导真实的Arduino/STM32控制?
从Simulink到Arduino:如何将直流电机控制算法从仿真落地到真实硬件 当你第一次在Simulink中看到那个完美的电机转速响应曲线时,那种成就感是无可替代的。但很快,一个更迫切的问题出现了:这些漂亮的仿真结果,如何变成手…...
宁德时代2026春招开启:6000+offer,这一轮机会在扩大
很多人现在还在犹豫一个问题:新能源是不是已经开始降温了?现在再投,还能不能拿到好的岗位?但从今年的招聘情况来看,趋势其实很清晰:岗位没有减少,而是在结构性增加。尤其是动力电池、储能、电池…...
解决Python ssl模块与系统OpenSSL版本不一致的编译指南
1. 为什么Python的ssl模块会与系统OpenSSL版本不一致? 很多开发者都遇到过这样的困惑:明明系统已经升级了OpenSSL,为什么Python的ssl模块还在使用旧版本?这个问题其实源于Python的编译机制。Python在编译安装时,会将当…...
小型纯电动汽车轮毂电机及大角度转向系统的数字化设计【含catia、solidworks、CAD图纸、答辩PPT、说明书】
小型纯电动汽车轮毂电机与大角度转向系统的数字化设计,是新能源汽车领域的关键技术突破方向。轮毂电机通过将驱动装置集成于车轮内部,实现了动力传递路径的简化与能量利用效率的提升,其分布式驱动特性使车辆具备更灵活的扭矩分配能力…...
MStar-Bin-Tool-Master中文版|晨星芯片BIN固件解包/封包工具(适配机顶盒与智能电视)
温馨提示:文末有联系方式工具简介 MStar-Bin-Tool-Master中文增强版是一款专为晨星(MStar)系列主控芯片设计的固件解析与重构工具,全面支持主流机顶盒与智能液晶电视所用BIN格式刷机包,提供直观易用的图形化操作界面&a…...
