当前位置: 首页 > news >正文

【设计模式-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】创建型——原型模式

说明&#xff1a;本文介绍设计模式中&#xff0c;创建型中的原型模式&#xff1b; 飞机大战 创建型设计模式关注于对象的创建&#xff0c;原型模式也不例外。如简单工厂和工厂模式中提到过的飞机大战这个例子&#xff0c;游戏中飞机、坦克对象会创建许许多多的实例&#xff0…...

八大插入算法(有注释)

直接插入排序 //直接插入排序 void InsertSortingDirectly(int* nums,int numsSize){int j0;for(int i1;i<numsSize-1;i){//定义一个中间变量保存当前要插入的值int tempnums[i];//在前面已排好序的序列中&#xff0c;找到合适的位置插入for(ji-1;j>0;j--){if(nums[j]&g…...

【2】基于多设计模式下的同步异步日志系统

6. 相关技术知识补充 6.1 不定参函数 在初学C语⾔的时候&#xff0c;我们都⽤过printf函数进⾏打印。其中printf函数就是⼀个不定参函数&#xff0c;在函数内部可以根据格式化字符串中格式化字符分别获取不同的参数进⾏数据的格式化。 ⽽这种不定参函数在实际的使⽤中也⾮常…...

npm管理发布包-创建与发布

创建与发布 我们可以将自己开发的工具包发布到 npm 服务上&#xff0c;方便自己和其他开发者使用&#xff0c;操作步骤如下 创建文件夹&#xff0c;并创建文件indexjs&#xff0c;在文件中声明函数&#xff0c;使用 module.exports 暴露npm初始化工具包&#xff0c;package.j…...

基于Spring,SpringMVC,MyBatis的校园二手交易网站

文章目录 项目介绍主要功能截图:部分代码展示设计总结项目获取方式🍅 作者主页:超级无敌暴龙战士塔塔开 🍅 简介:Java领域优质创作者🏆、 简历模板、学习资料、面试题库【关注我,都给你】 🍅文末获取源码联系🍅 项目介绍 基于Spring,SpringMVC,MyBatis的校园二…...

酒店 KPI绩效考核指标及应用

“路遥知马力&#xff0c;日久见人心”&#xff0c;目前国内各类型酒店风起云涌&#xff0c;大有在市场竞争中一比高下之势&#xff0c;各路精英受经济型酒店低投入高回报的市场利益驱动&#xff0c;都分分抢占市场&#xff0c;从而使国内经济型酒店的数量不断增加&#xff0c;…...

WordPress两种方法实现上传媒体图片文件自动重命名

我们发布文章时&#xff0c;会上传一些图片、音频之类的文件。但是WordPress没有自动 给新上传文件重命名的功能&#xff0c;逐个文件去重命名那就太麻烦了&#xff0c;那么我们改如何自动给上传的媒体文件图片重命名呢&#xff1f; 我在网站搜索了些上WordPress上传媒体文件自…...

TZOJ 1405 An easy problem

翻译有些出错&#xff0c;但大概是那个意思 答案&#xff1a; #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实现大学生健康档案管理系统&#xff0c;主要针对计算机相关专业的正在做bishe的学生和需要项目实战练习的Java学习者。 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目可以直接作为bishe使用。 项目都经过严格调试&#…...

CCC联盟数字车钥匙(三)——UWB MAC时间网格同步及Hopping

本文继续上一篇UWB MAC时间网格继续介绍UWB MAC中关于时间同步相关内容。 3、MAC时间网格同步 每个测距会话的定义都基于相对的指定时钟参考 U W B t i m e 0 k UWB^k_{time0} UWBtime0k​&#xff0c;相对于发起者的内部时钟定义。 时钟参考 U W B t i m e 0 k UWB^k_{time0} …...

一周上手 steam搬砖项目或成2024年最受欢迎副业

蒸汽砖拆除项目&#xff0c;兼职创业两不误&#xff0c;助你轻松赚钱 你是否想要找到一个既可以兼职又可以创业的项目&#xff1f;蒸汽砖拆除项目正逐渐崭露头角&#xff0c;引起了越来越多人的关注。这个项目不仅门槛低&#xff0c;上手快&#xff0c;而且不用担心卖不出去&am…...

java数据结构(哈希表—HashMap)含LeetCode例题讲解

目录 1、HashMap的基本方法 1.1、基础方法&#xff08;增删改查&#xff09; 1.2、其他方法 2、HashMap的相关例题 2.1、题目介绍 2.2、解题 2.2.1、解题思路 2.2.2、解题图解 2.3、解题代码 1、HashMap的基本方法 HashMap 是一个散列表&#xff0c;它存储的内容是键…...

快速了解ChatGPT(大语言模型)

目录 GPT原理&#xff1a;文字接龙&#xff0c;输入一个字&#xff0c;后面会接最有可能出现的文字。 GPT4 学会提问&#xff1a;发挥语言模型的最大能力 参考李宏毅老师的课快速了解大语言模型做的笔记&#xff1a; Lee老师幽默的开场&#xff1a; GPT&#xff1a;chat Ge…...

计算机软件的分类

以功能进行分类&#xff0c;计算机软件通常可以分为系统软件和应用软件两大类。 系统软件&#xff1a;系统软件是计算机运行和管理的基本软件&#xff0c;包括操作系统、驱动程序、系统工具和服务程序等。操作系统是系统软件的核心&#xff0c;负责管理计算机的硬件资源、提供用…...

数据库应用: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 &#xff08;1&#xff09;概念 …...

服务器配置 jupyter lab,并在本地浏览器免密登陆

一、背景 快速搭建一个jupyter lab 不用每次用ssh登录输入密码 二、步骤 方法1、临时在服务器启动 jupyter lab&#xff0c;并在本地浏览器免密登陆 两句命令解决 pip install jupyterlabnohup jupyter lab --ServerApp.ip"*" --ServerApp.password"" -…...

WebUI自动化学习(Selenium+Python+Pytest框架)002

新建项目 New Project 新建一个python代码文件 file-new-python file 会自动创建一个.py后缀的代码文件 注意:命名规则,包含字母、数字、下划线&#xff0c;不能以数字开头&#xff0c;不能跟python关键字或包名重复。 ********************华丽分割线********************…...

miot-plugin-sdk. npm install安装失败

miot-plugin-sdk-npm install安装失败 最紧公司要开发一台智能设备&#xff0c;经过同事的对比&#xff0c;选中了米家作为云平台&#xff0c;于是&#xff0c;我就负责开发app界面端&#xff0c;根据官方文档教程 下载了miot-plugin-sdk 程序&#xff0c;准备开始开发,结果悲…...

抓取微信好友列表信息

本文实现的是一种较为安全、简洁、高效的抓取微信好友信息的方法。 实现工具&#xff1a;微信pc端、影刀RPA 主要流程&#xff1a; 手动—前期准备&#xff0c;电脑登陆微信&#xff0c;打开联系人页&#xff0c;使得联系人分类“A”显现在微信窗口界面 自动—运行程序&#…...

创建JDK8版本的SpringBoot项目的方法

目录 一.通过阿里云下载 二.通过IDEA创建 1.下载安装JDK17 2.创建SpringBoot 3.X的项目 3.把JDK17改成JDK8 截止到2023.11.24&#xff0c;SpringBoot不再支持3.0X之前的版本&#xff0c;3.0X之后的版本所对应的JDK版本为JDK17&#xff0c;下面介绍如何在idea上继续使用JDK…...

idea大量爆红问题解决

问题描述 在学习和工作中&#xff0c;idea是程序员不可缺少的一个工具&#xff0c;但是突然在有些时候就会出现大量爆红的问题&#xff0c;发现无法跳转&#xff0c;无论是关机重启或者是替换root都无法解决 就是如上所展示的问题&#xff0c;但是程序依然可以启动。 问题解决…...

stm32G473的flash模式是单bank还是双bank?

今天突然有人stm32G473的flash模式是单bank还是双bank&#xff1f;由于时间太久&#xff0c;我真忘记了。搜搜发现&#xff0c;还真有人和我一样。见下面的链接&#xff1a;https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...

CentOS下的分布式内存计算Spark环境部署

一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架&#xff0c;相比 MapReduce 具有以下核心优势&#xff1a; 内存计算&#xff1a;数据可常驻内存&#xff0c;迭代计算性能提升 10-100 倍&#xff08;文档段落&#xff1a;3-79…...

自然语言处理——Transformer

自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效&#xff0c;它能挖掘数据中的时序信息以及语义信息&#xff0c;但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN&#xff0c;但是…...

Python 包管理器 uv 介绍

Python 包管理器 uv 全面介绍 uv 是由 Astral&#xff08;热门工具 Ruff 的开发者&#xff09;推出的下一代高性能 Python 包管理器和构建工具&#xff0c;用 Rust 编写。它旨在解决传统工具&#xff08;如 pip、virtualenv、pip-tools&#xff09;的性能瓶颈&#xff0c;同时…...

Razor编程中@Html的方法使用大全

文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...

Git 3天2K星标:Datawhale 的 Happy-LLM 项目介绍(附教程)

引言 在人工智能飞速发展的今天&#xff0c;大语言模型&#xff08;Large Language Models, LLMs&#xff09;已成为技术领域的焦点。从智能写作到代码生成&#xff0c;LLM 的应用场景不断扩展&#xff0c;深刻改变了我们的工作和生活方式。然而&#xff0c;理解这些模型的内部…...

解读《网络安全法》最新修订,把握网络安全新趋势

《网络安全法》自2017年施行以来&#xff0c;在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂&#xff0c;网络攻击、数据泄露等事件频发&#xff0c;现行法律已难以完全适应新的风险挑战。 2025年3月28日&#xff0c;国家网信办会同相关部门起草了《网络安全…...

作为测试我们应该关注redis哪些方面

1、功能测试 数据结构操作&#xff1a;验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化&#xff1a;测试aof和aof持久化机制&#xff0c;确保数据在开启后正确恢复。 事务&#xff1a;检查事务的原子性和回滚机制。 发布订阅&#xff1a;确保消息正确传递。 2、性…...

windows系统MySQL安装文档

概览&#xff1a;本文讨论了MySQL的安装、使用过程中涉及的解压、配置、初始化、注册服务、启动、修改密码、登录、退出以及卸载等相关内容&#xff0c;为学习者提供全面的操作指导。关键要点包括&#xff1a; 解压 &#xff1a;下载完成后解压压缩包&#xff0c;得到MySQL 8.…...