原型模式详细介绍和代码实现
🎯 设计模式专栏,持续更新中, 欢迎订阅:JAVA实现设计模式
🛠️ 希望小伙伴们一键三连,有问题私信都会回复,或者在评论区直接发言
Java实现原型模式
介绍: 原型模式(Prototype Pattern)是一种创建型设计模式,允许通过复制已有对象来创建新对象,而不需要依赖其具体类。这种模式的关键在于克隆现有对象,而不是通过直接实例化新对象,从而避免重复的复杂对象构建过程。
🎯 案例场景:
假设我们在开发一个游戏角色系统,每个角色都有不同的属性,比如力量、敏捷和智力。创建一个新角色需要耗费大量的计算资源(如生成外貌、装备等)。如果我们想要快速创建一个与现有角色相似的新角色,只需要在原有角色基础上进行微调,而不重新生成所有属性。
我们可以使用原型模式,通过复制已有角色对象,再对其进行调整,来生成新的角色。
原型模式的核心步骤:
- Prototype 接口:定义
clone()方法,所有需要被复制的对象实现该接口。 - 具体原型类:实现
clone()方法,用于创建对象的副本。 - 客户端代码:通过调用
clone()方法来复制已有对象。
🧑💻 Java代码实现:

import java.util.HashMap;
import java.util.Map;// 1. 定义原型接口 Prototype
interface GameCharacter extends Cloneable {GameCharacter clone();void display();
}// 2. 具体的角色类实现原型接口
class Warrior implements GameCharacter {private String name;private int strength;private int agility;private int intelligence;public Warrior(String name, int strength, int agility, int intelligence) {this.name = name;this.strength = strength;this.agility = agility;this.intelligence = intelligence;}// 实现 clone 方法@Overridepublic Warrior clone() {try {return (Warrior) super.clone(); // 调用 Object 类的 clone 方法} catch (CloneNotSupportedException e) {throw new RuntimeException("Clone not supported", e);}}// 展示角色信息@Overridepublic void display() {System.out.println("Warrior [Name: " + name + ", Strength: " + strength +", Agility: " + agility + ", Intelligence: " + intelligence + "]");}
}// 3. 客户端代码,使用原型模式
public class PrototypePatternDemo {public static void main(String[] args) {// 创建一个原始的角色Warrior originalWarrior = new Warrior("Arthas", 80, 50, 30);originalWarrior.display();// 使用克隆方法复制角色Warrior clonedWarrior = originalWarrior.clone();// 修改克隆对象的某些属性clonedWarrior.display();// 现在,原始角色和克隆角色的内存地址不同,但属性相同System.out.println("Original Warrior HashCode: " + originalWarrior.hashCode());System.out.println("Cloned Warrior HashCode: " + clonedWarrior.hashCode());}
}
📋 解释:
GameCharacter接口:定义了clone()方法,表示所有实现这个接口的类都可以被克隆。Warrior类:实现了GameCharacter接口,并通过super.clone()进行浅克隆。这个方法通过复制对象的当前状态来创建一个新实例。- 客户端代码:首先创建了一个
Warrior对象originalWarrior,并使用其clone()方法创建了一个副本clonedWarrior。然后可以自由修改克隆对象的属性,而不影响原始对象。
🌟 应用场景:
- 对象创建开销大,不希望每次都通过构造函数创建。
- 需要保存对象的初始状态,以便在不同地方进行修改和操作。
- 游戏开发中,需要频繁创建相似但稍有不同的角色或物品。
深拷贝和浅拷贝
浅拷贝:复制对象时,只复制对象的基本属性和引用类型的引用。即如果对象的属性是引用类型(如数组、对象),浅拷贝只会复制这些引用,而不是引用所指向的实际对象。因此,拷贝后的对象和原始对象共享这些引用类型的数据,修改引用类型的内容会影响原始对象。
深拷贝:不仅会复制对象的基本属性,还会递归地复制引用类型所指向的对象,即引用对象也会完全复制成一个新的副本。这样,拷贝后的对象与原始对象完全独立,修改拷贝对象的引用类型内容,不会影响原始对象。
🌟 案例解释:
假设我们有一个 Person 类,里面包含基本类型属性 name 和引用类型 address。
1. 浅拷贝示例:
浅拷贝只复制基本属性和引用对象的引用,不复制引用对象本身。
class Address {String city;public Address(String city) {this.city = city;}
}class Person implements Cloneable {String name;Address address;public Person(String name, Address address) {this.name = name;this.address = address;}// 浅拷贝@Overridepublic Person clone() {try {return (Person) super.clone(); // 调用 Object 的 clone 方法} catch (CloneNotSupportedException e) {throw new RuntimeException("Clone not supported", e);}}public void display() {System.out.println("Name: " + name + ", City: " + address.city);}
}public class ShallowCopyDemo {public static void main(String[] args) {Address address = new Address("New York");Person person1 = new Person("John", address);Person person2 = person1.clone(); // 浅拷贝// 显示原始和克隆对象person1.display(); // Name: John, City: New Yorkperson2.display(); // Name: John, City: New York// 修改克隆对象的 addressperson2.address.city = "San Francisco";// 浅拷贝后,原始对象的 address 也被改变person1.display(); // Name: John, City: San Franciscoperson2.display(); // Name: John, City: San Francisco}
}
结果解释:
person2是person1的浅拷贝。- 当
person2的address.city修改为"San Francisco"后,person1的address.city也变成了"San Francisco"。这是因为浅拷贝只复制了引用,两个对象共享同一个Address对象。
2. 深拷贝示例:
深拷贝需要复制所有对象,包括引用对象中的数据。
class Address implements Cloneable {String city;public Address(String city) {this.city = city;}@Overridepublic Address clone() {try {return (Address) super.clone(); // 克隆 Address 对象} catch (CloneNotSupportedException e) {throw new RuntimeException("Clone not supported", e);}}
}class Person implements Cloneable {String name;Address address;public Person(String name, Address address) {this.name = name;this.address = address;}// 深拷贝@Overridepublic Person clone() {try {Person clonedPerson = (Person) super.clone();clonedPerson.address = address.clone(); // 深拷贝 Addressreturn clonedPerson;} catch (CloneNotSupportedException e) {throw new RuntimeException("Clone not supported", e);}}public void display() {System.out.println("Name: " + name + ", City: " + address.city);}
}public class DeepCopyDemo {public static void main(String[] args) {Address address = new Address("New York");Person person1 = new Person("John", address);Person person2 = person1.clone(); // 深拷贝// 显示原始和克隆对象person1.display(); // Name: John, City: New Yorkperson2.display(); // Name: John, City: New York// 修改克隆对象的 addressperson2.address.city = "San Francisco";// 深拷贝后,原始对象的 address 未被改变person1.display(); // Name: John, City: New Yorkperson2.display(); // Name: John, City: San Francisco}
}
结果解释:
person2是person1的深拷贝。- 当
person2的address.city修改为"San Francisco"后,person1的address.city仍然是"New York"。这是因为深拷贝创建了Address对象的独立副本,两个对象不再共享同一个引用。
🔍 关键区别总结:
- 浅拷贝
- 只复制对象的基本类型属性和引用的地址,不复制引用的实际内容。
- 拷贝后的对象与原对象共享引用类型的对象。
- 如果修改了引用类型的数据,原始对象也会受到影响。
- 深拷贝
- 复制对象的所有属性,包括递归复制引用类型对象。
- 拷贝后的对象和原始对象完全独立,互不影响。
- 修改其中一个对象的引用类型内容,不会影响另一个对象。
相关文章:
原型模式详细介绍和代码实现
🎯 设计模式专栏,持续更新中, 欢迎订阅:JAVA实现设计模式 🛠️ 希望小伙伴们一键三连,有问题私信都会回复,或者在评论区直接发言 Java实现原型模式 介绍: 原型模式(Prototype Patte…...
ArcGIS Pro SDK (十三)地图创作 5 图层样式
ArcGIS Pro SDK (十三)地图创作 5 图层样式 文章目录 ArcGIS Pro SDK (十三)地图创作 5 图层样式1 风格管理1.1 如何按名称获取项目中的样式1.2 如何创建新样式1.3 如何向项目添加样式1.4 如何从项目中删除样式1.5 如何将样式项添加到样式1.6 如何从样式中删除样式项1.7 如…...
【Python报错已解决】 Requests.exceptions.ProxyError: HTTPSConnectionPool
🎬 鸽芷咕:个人主页 🔥 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想,就是为了理想的生活! 专栏介绍 在软件开发和日常使用中,BUG是不可避免的。本专栏致力于为广大开发者和技术爱好者提供一个关于BUG解决的经…...
现在量化中普遍使用QMT和PTrade?哪家可以同时提供QMT/PTrade?
QMT的特点 全面的功能集成: QMT集成了行情显示、策略研究、交易执行和风控管理于一体,为投资者提供了一站式的量化交易解决方案。 高效的交易执行能力: 通过全内存交易实现低延迟的交易执行,单笔延时小于1ms,确保了交易…...
【计算机网络】UDP 协议详解及其网络编程应用
文章目录 一、引言二、UDP1、UDP的协议格式2、UDP 报文的解包和分用3、UDP面向数据报的特点 三、UDP输入输出四、UDP网络编程 一、引言 UDP(User Datagram Protocol,用户数据报协议)是一种网络通信协议,它属于传输层的协议。是一…...
基于ESP32S3的链接大语言模型对话模块
本实物模块从实物外观、模块组成、API申请及功能说明四部分来介绍这款基于ESP32S3的大语言模型对话模块。 1、实物外观 2、模块介绍 本硬件平台主要由三个模块组成,包括MAX9814录音模块、MAX98357音频功放模块和ESP32S3模块。如下图所示。 MAX9814录音模块&#…...
Cpp输出多字符常量警告
Cpp输出多字符常量警告 Cpp中用单引号(single quotes)表示单个字符(single character),例如a,$,用双引号(double quotes)表示字符串文本(text),例如"Hello World! " 当在一个单引号里面存在多个字符时,Cpp…...
Maven从入门到精通(二)
一、什么是pom.xml pom.xml是Maven项目的核心配置文件,它是 项目对象模型 - Project Object Model(POM)的缩写。POM定义了项目的所有属性,包括项目的名称、版本、依赖关系、构建配置等。使用pom.xml,我们可以轻松地管…...
【Kubernetes】常见面试题汇总(二十四)
目录 71.假设一家公司想要修改它的部署方法,并希望建立一个更具可扩展性和响应性的平台。您如何看待这家公司能够实现这一目标以满足客户需求? 72.考虑一家拥有非常分散的系统的跨国公司,期待解决整体代码库问题。您认为公司如何解决他们的问…...
最低成本的游戏串流方案分享 如何自己打造云电脑?
今天教大家如何最低成本实现串流 出门在外也可以随时随地游玩端游大作 硬件准备:一台电脑 手机/平板一台 软件:Gameviewer远程 为啥不用moonlight等其他软件呢 因为设置公网穿透等复杂操作对小白来说不太友好 而GameViewer从安装到使用仅需一键 对比同类…...
python运行时错误:找不到fbgemm.dll
python运行时错误:找不到fbgemm.dll 报错: OSError: [WinError 126] 找不到指定的模块。 Error loading "D:\program\py\312\Lib\site-packages\torch\lib\fbgemm.dll" or one of its dependencies. 原因是Windows下缺失:libomp140…...
给虚拟机linux系统安装交叉编译工具链
我们在电脑上写的代码编译生成的是X86架构的二进制文件,只能在X86平台上运行,而开发板是ARM架构因此需要安装交叉编译链工具,这样在电脑上写的代码交叉编译之后生成的是ARM架构的二进制文件。 绿色的字眼是与本文无关的只是这样有助于我们的…...
Redhat 7,8系(复刻系列) 一键部署Oracle21c-xe rpm
Oracle21c-xe前言 无论您是开发人员、DBA、数据科学家、教育工作者,还是仅仅对数据库感兴趣,Oracle Database Express Edition (XE) 都是理想的入门方式。它是全球企业可依赖的强大的 Oracle Database,提供简单的下载、易于使用和功能齐全的体验。您可以在任何环境中使用该…...
Web植物管理系统-下位机部分
本节主要展示上位机部分,采用BSP编程,不附带BSP中各个头文件的说明,仅仅是对main逻辑进行解释 main.c 上下位机通信 通过串口通信,有两位数据验证头(verify数组中保存对应的数据头 0xAA55) 通信格式 上位发送11字节…...
leetcode169. 多数元素
给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。 你可以假设数组是非空的,并且给定的数组总是存在多数元素。 示例 1: 输入:nums [3,2,3] 输出:3 示例…...
从大脑图谱/ROI中提取BOLD信号
动机 在功能连接(Functional Connectivity,FC)构建过程中,由于FC中元素数目是节点数目的平方关系,所以在计算FC之前进行数据降维是一个常见的选择。 一般会将体素级/顶点级BOLD信号(在2mm的图像分辨率下大脑…...
Java-数据结构-优先级队列(堆)-(一) (;´д`)ゞ
文本目录: ❄️一、优先级队列: ➷ 1、概念: ❄️二、优先级队列的模拟实现: ➷ 1、堆的概念: ➷ 2、堆的性质: ➷ 3、堆的创建: ▶ 向下调整: ➷ 4、堆的插入和删除: …...
工厂模式(二):工厂方法模式
一、概念 工厂方法模式(Factory Method),定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。从而使得系统更加灵活。客户端可以通过调用工厂方法来创建所需的产品,而不必…...
【洛谷】P11036 【MX-X3-T3】「RiOI-4」GCD 与 LCM 问题 的题解
【洛谷】P11036 【MX-X3-T3】「RiOI-4」GCD 与 LCM 问题 的题解 题目传送门 题解 神奇构造题qaq 简化一下下题目,就是要求 a b c d gcd ( a , b ) lcm ( c , d ) a b c d \gcd(a, b) \operatorname{lcm}(c,d) abcdgcd(a,b)lcm(c,d) 分类讨论 …...
MyBatis系统学习(三)——动态SQL
MyBatis 是一款优秀的持久层框架,它通过 XML 或注解方式将 SQL 语句与 Java 对象映射起来。动态 SQL 是 MyBatis 中非常强大的功能之一,能够根据不同的条件动态生成 SQL 语句。动态 SQL 通过各种标签来灵活生成 SQL,从而避免了在代码中拼接 S…...
C++:std::is_convertible
C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...
涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战
“🤖手搓TuyaAI语音指令 😍秒变表情包大师,让萌系Otto机器人🔥玩出智能新花样!开整!” 🤖 Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制(TuyaAI…...
【论文阅读28】-CNN-BiLSTM-Attention-(2024)
本文把滑坡位移序列拆开、筛优质因子,再用 CNN-BiLSTM-Attention 来动态预测每个子序列,最后重构出总位移,预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵(S…...
Spring数据访问模块设计
前面我们已经完成了IoC和web模块的设计,聪明的码友立马就知道了,该到数据访问模块了,要不就这俩玩个6啊,查库势在必行,至此,它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据(数据库、No…...
【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)
本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...
C/C++ 中附加包含目录、附加库目录与附加依赖项详解
在 C/C 编程的编译和链接过程中,附加包含目录、附加库目录和附加依赖项是三个至关重要的设置,它们相互配合,确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中,这些概念容易让人混淆,但深入理解它们的作用和联…...
力扣热题100 k个一组反转链表题解
题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...
【Linux】Linux 系统默认的目录及作用说明
博主介绍:✌全网粉丝23W,CSDN博客专家、Java领域优质创作者,掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围:SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...
基于PHP的连锁酒店管理系统
有需要请加文章底部Q哦 可远程调试 基于PHP的连锁酒店管理系统 一 介绍 连锁酒店管理系统基于原生PHP开发,数据库mysql,前端bootstrap。系统角色分为用户和管理员。 技术栈 phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销 2 个人中…...
tauri项目,如何在rust端读取电脑环境变量
如果想在前端通过调用来获取环境变量的值,可以通过标准的依赖: std::env::var(name).ok() 想在前端通过调用来获取,可以写一个command函数: #[tauri::command] pub fn get_env_var(name: String) -> Result<String, Stri…...
