Unity 设计模式-原型模式(Prototype Pattern)详解

news/2024/11/25 15:52:34/

原型模式 (Prototype Pattern)

原型模式 (Prototype Pattern) 是一种创建型设计模式,它允许通过复制现有的对象来创建新对象,而不是通过直接实例化类。这意味着你可以通过克隆原型对象来生成新的实例,而不必依赖类的构造函数。该模式的核心思想是,通过创建一个对象的副本(即原型)来避免昂贵的初始化操作。

在 C# 中,通常通过实现 ICloneable 接口或者自定义的克隆方法来实现原型模式

1、原型模式的原理
原型模式的工作原理是:

定义一个原型接口或基类,用来提供克隆的方法(如 Clone)。
具体的类实现该接口,提供自己的克隆方法,该方法返回当前对象的深拷贝或浅拷贝。
客户端可以通过克隆原型对象来创建新对象,而无需知道如何构造对象。
拷贝的类型可以分为:

  • 浅拷贝 (Shallow Copy):只复制对象的基本数据类型,对于引用类型的字段,只复制引用地址。
  • 深拷贝 (Deep Copy):不仅复制对象的所有值,还递归地复制所有引用对象,创建独立的副本。

2、什么时候使用原型模式

  • 需要频繁创建相似对象时:如果系统需要创建大量相似或结构复杂的对象时,通过克隆原型对象来生成新对象可以提高性能。
  • 对象的创建代价高昂时:如果对象的初始化非常复杂,包含很多属性设置或涉及昂贵的资源操作(如网络连接、文件系统操作),使用原型模式避免了重复创建过程。
  • 避免对象过多的子类化:当不希望为了创建新对象而引入更多的子类或扩展类时,原型模式可以避免通过继承创建不同类型对象的繁琐过程。
  • 需要保存对象的历史状态或备份时:如果需要将某个对象的状态备份或在某个时刻进行复制(如撤销操作、快照功能),可以通过原型模式来克隆对象。

3、使用原型模式的好处

  • 提高对象创建效率:对于某些对象的创建过程非常复杂或耗时时,通过克隆现有对象(即原型)可以避免重复的复杂初始化,节省时间和资源。
  • 原型模式向客户隐藏了创建新实例的复杂性
  • 原型模式允许动态增加或较少产品类。
  • 原型模式简化了实例的创建结构,工厂方法模式需要有一个与产品类等级结构相同的等级结构,而原型模式不需要这样。
  • 产品类不需要事先确定产品的等级结构,因为原型模式适用于任何的等级结构

4、使用原型模式的注意事项
克隆的深浅拷贝:对于引用类型字段,使用浅拷贝时需要特别小心,浅拷贝只复制引用,而不复制对象本身,可能导致两个对象共用同一份数据。若需要完全独立的对象,必须使用深拷贝。

复杂对象的克隆:当对象包含复杂的嵌套结构或其他依赖时,确保实现合适的深拷贝逻辑,否则可能会出现数据共享或数据覆盖问题。

性能考虑:尽管原型模式避免了对象的重复构造,但深拷贝可能引入较大的性能开销,特别是在对象嵌套复杂时。因此,在性能敏感的场景下要慎重考虑深拷贝的实现。

原型的可变性:当通过原型模式创建对象时,如果不慎修改了原型本身的状态,所有基于该原型创建的对象也可能受到影响。因此在设计时,需要确保原型对象的状态是安全的。

总之,原型模式 通过复制现有对象来生成新对象,避免了类构造的开销,特别适用于对象创建代价高昂或需要动态创建对象的场景。它提供了灵活的对象创建方式,减少了类的复杂性和耦合度。使用时需要根据具体需求选择浅拷贝或深拷贝,并确保对象的可复制性和独立性。

在 Unity 中使用 原型模式

在 Unity 中,原型模式可以应用于场景中需要频繁生成的对象,比如 3D 模型(如立方体、球体等)。通过原型模式,我们可以避免每次都从头实例化对象,而是通过复制现有的对象来创建新的实例。

我们将创建几个简单的原型模式应用

  • 用于克隆 Unity 中的 3D 对象(立方体、球体等),并根据用户输入生成多个克隆对象。

步骤:

  1. 创建一个基类 ShapePrototype,它提供克隆接口。
  2. 创建不同的形状类,如立方体和球体,继承自 ShapePrototype
  3. 使用原型模式通过克隆现有对象来创建新对象,而不是直接创建新对象。

1.定义基类 ShapePrototype

using UnityEngine;// 定义一个抽象的原型基类
public abstract class ShapePrototype : MonoBehaviour
{// 定义一个抽象的克隆方法public abstract ShapePrototype Clone();// 通用的显示形状信息的方法public abstract void DisplayInfo();
}

2.创建具体的形状类

2.1立方体类

using UnityEngine;public class Cube : ShapePrototype
{// 重写克隆方法public override ShapePrototype Clone(){// 实现浅拷贝,复制当前对象的属性GameObject clone = Instantiate(this.gameObject);return clone.GetComponent<ShapePrototype>();}public override void DisplayInfo(){Debug.Log("This is a Cube.");}
}

2.2球体类

using UnityEngine;public class Sphere : ShapePrototype
{// 重写克隆方法public override ShapePrototype Clone(){// 实现浅拷贝,复制当前对象的属性GameObject clone = Instantiate(this.gameObject);return clone.GetComponent<ShapePrototype>();}public override void DisplayInfo(){Debug.Log("This is a Sphere.");}
}

3.创建ShapeSpawner 负责克隆对象

using UnityEngine;public class ShapeSpawner : MonoBehaviour
{public ShapePrototype cubePrototype;   // 立方体原型public ShapePrototype spherePrototype; // 球体原型private void Update(){// 按下 C 键克隆立方体if (Input.GetKeyDown(KeyCode.C)){ShapePrototype cubeClone = cubePrototype.Clone();cubeClone.transform.position = new Vector3(Random.Range(-5, 5), 1, Random.Range(-5, 5));cubeClone.DisplayInfo();}// 按下 S 键克隆球体if (Input.GetKeyDown(KeyCode.S)){ShapePrototype sphereClone = spherePrototype.Clone();sphereClone.transform.position = new Vector3(Random.Range(-5, 5), 1, Random.Range(-5, 5));sphereClone.DisplayInfo();}}
}

4.设置场景中的原型对象

  • 创建一个 Unity 场景,并添加一个空物体 ShapeSpawner,将上面的 ShapeSpawner 脚本挂载到该物体上。
  • 在场景中创建一个立方体和一个球体,并将它们的 Cube 和 Sphere 脚本分别挂载到立方体和球体上。
  • 在 ShapeSpawner 脚本的 cubePrototype 和 spherePrototype 变量中,分别拖拽场景中的立方体和球体作为原型对象。

5.输出查看结果

这种模式在 Unity 的游戏开发中非常适合用于生成大量相似对象(如敌人、物品、特效等)。

  • 克隆一个新对象,然后将当前对象非静态字段克隆到该新对象

1.定义基类 Prototype

public abstract class Prototype
{private string id;public Prototype(string id){this.id = id;}public string Id{get { return id; }}//抽象类关键有这样一个Clone方法public abstract Prototype Clone();
}

2.创建具体原型ConcretePrototypel类

class ConcretePrototypel : Prototype
{public ConcretePrototypel(string id) : base(id) { }public override Prototype Clone(){//创建一个新对象,然后将当前对象非静态字段复制到该新对象//如果字段是值类型,则逐位复制字段,引用类型只复制引用地址return (Prototype)this.MemberwiseClone();}
}

3.创建main脚本负责克隆对象

class Main : MonoBehaviour
{private void Start(){ConcretePrototypel pl = new ConcretePrototypel("I");ConcretePrototypel cl = (ConcretePrototypel)pl.Clone();Debug.Log("clone" + cl.Id);}
}

4.输出查看结果

由于克隆实在太常用,.Net在System命名空间提供了IClone接口,唯一的Clone()方法,只要实现这个接口就可以完成原型模式了。

  • 简历类,用于克隆 Unity 中的 个人信息

1.创建简历基础脚本

class Resume : ICloneable
{private string name;private string sex;private string age;private string timearea;private string company;public Resume(string name){this.name = name;}//设置个人信息public void SetPersonalInfo(string sex,string age){this.sex = sex;this.age = age;}//设置工作经历public void SetWorkExperrience(string timeArea,string company){this.timearea = timeArea;this.company = company;}//显示public void Display(){//实现接口方法,克隆对象Debug.Log(name + " " + sex + " " + age);Debug.Log(timearea + " " + company);}public object Clone(){return (object)this.MemberwiseClone();}
}

2.克隆新简历,并修改简历中的个人信息

class Main : MonoBehaviour
{private void Start(){Resume a = new Resume("DJ");a.SetPersonalInfo("男", "22");a.SetWorkExperrience("1995-2022", "DJDJ");//调用克隆方法就可以实现新简历,并且可以修改新简历细节Resume b = (Resume)a.Clone();b.SetPersonalInfo("nv", "20");Resume c = (Resume)a.Clone();c.SetWorkExperrience("1999-2222", "JJJJ");a.Display();b.Display(); c.Display();}
}

3.输出查看结果

现实设计当中,一般会再有一个“工作经历”类,当中有“时间区间”和“公司名称”等属性,“简历”类直接调用。

class Resume : ICloneable
{private string name;private string sex;private string age;private WorkExperience work;///引用“工作经历”对象public Resume(string name){this.name = name;work = new WorkExperience();//简历实例化同时实例化工作经历}public void SetPersonalInfo(string sex,string age){this.sex = sex;this.age = age;}public void SetWorkExperrience(string workDate,string company){work.WorkDate = workDate;//调用方法,给对象赋值work.Company = company;}public void Display(){Debug.Log(name + " " + sex + " " + age);Debug.Log(work.WorkDate + " " + work.Company);//显示工作经历属性值}public object Clone(){return (object)this.MemberwiseClone();}
}
class WorkExperience
{private string workDate;public string WorkDate{get { return workDate; }set { workDate = value; }}private string company;public string Company{get { return company; }set { company = value; }}
}

使用之前的客户端逻辑,输出查看结果

对于引用类型,克隆后没有实现真正的克隆,而是只克隆了引用地址,这叫做“浅复制”,被复制对象的所有变量都含有与原来的对象相同的值;而所有的对其他对象的引用都仍然指向原来的对象。

深复制把引用对象的变量指向复制过的新对象,而不是原有的被引用的对象。

深复制流程:

1.首先修改WorkExperience类,增加克隆方法

class WorkExperience
{private string workDate;public string WorkDate{get { return workDate; }set { workDate = value; }}private string company;public string Company{get { return company; }set { company = value; }}public object Clone(){//工作经历类也实现克隆方法return (object)MemberwiseClone();}
}

2.然后修改简历类,新增构造函数,方便克隆工作经历类,再修改简历类的克隆方法

class Resume : ICloneable
{private string name;private string sex;private string age;private WorkExperience work;public Resume(string name){this.name = name;work = new WorkExperience();}//提供Clone方法调用的私有构造函数,以便克隆工作经历数据public Resume(WorkExperience work){this.work = (WorkExperience)work.Clone();}public void SetPersonalInfo(string sex,string age){this.sex = sex;this.age = age;}public void SetWorkExperrience(string workDate,string company){work.WorkDate = workDate;work.Company = company;}public void Display(){Debug.Log(name + " " + sex + " " + age);Debug.Log(work.WorkDate + " " + work.Company);}//调用私有构造方法,让工作经历克隆,然后再给新对象其他字段赋值//最终返回一个深复制的简历对象public object Clone(){Resume obj = new Resume(this.work);obj.name = this.name;obj.sex = this.sex; obj.age = this.age;return obj;}
}

3.输出查看结果

今天是2024年11月24日

重复一段毒鸡汤来勉励我和你

你的对手在看书

你的仇人在磨刀

你的闺蜜在减肥

隔壁的老王在练腰

而你在干嘛?


http://www.ppmy.cn/news/1549856.html

相关文章

【Linux】Linux进程概念

【Linux】Linux进程概念 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;Linux&#x1f34a; &#x1f33c;文章目录&#x1f33c; 1. 冯诺依曼体系结构 2. 操作系统&#xff08;Operator System&#xff09; 2.1 操作系统的概念 2.2…

三十一、构建完善微服务——API 网关

一、API 网关基础 系统拆分为微服务后&#xff0c;内部的微服务之间是互联互通的&#xff0c;相互之间的访问都是点对点的。如果外部系统想调用系统的某个功能&#xff0c;也采取点对点的方式&#xff0c;则外部系统会非常“头大”。因为在外部系统看来&#xff0c;它不需要也没…

Springboot项目搭建-Maven打包编译

1.概要 1.1 编译和编辑的区别 编译是将高级编程语言编写的源代码转换成计算机可执行的机器码或中间代码的自动化过程&#xff0c;主要关注语言转换和代码优化&#xff1b; 编辑则是对文本、文档或代码进行人工修改、格式化和校对的过程&#xff0c;主要涉及内容的增删改查和…

神经网络入门实战:(一)神经网络解决的两种问题,以及AI、机器学习、深度学习三者之间的逻辑关系

AI、机器学习、深度学习三者之间的逻辑关系&#xff1a; 两种问题 &#xff08;1&#xff09;回归问题 回归问题是指预测一个或多个连续值的任务。这些连续值可以是任意实数&#xff0c;比如价格、温度、分数等。 回归问题的目标通常是 找到一个函数 &#xff0c;该函数可以…

深入解析小程序组件:view 和 scroll-view 的基本用法

深入解析小程序组件:view 和 scroll-view 的基本用法 引言 在微信小程序的开发中,组件是构建用户界面的基本单元。两个常用的组件是 view 和 scroll-view。这两个组件不仅功能强大,而且使用灵活,是开发者实现复杂布局和交互的基础。本文将深入探讨这两个组件的基本用法,…

鸿蒙NEXT开发-Navigation组件导航

注意&#xff1a;博主有个鸿蒙专栏&#xff0c;里面从上到下有关于鸿蒙next的教学文档&#xff0c;大家感兴趣可以学习下 如果大家觉得博主文章写的好的话&#xff0c;可以点下关注&#xff0c;博主会一直更新鸿蒙next相关知识 专栏地址: https://blog.csdn.net/qq_56760790/…

高频面试-cookie, token, session

1. cookie: 第一次发送到服务端&#xff0c;服务端返回cookie到客户端&#xff0c;客户端以后每次请求就会带着这个cookie&#xff0c; 使用cookie的缺陷&#xff1a; 使用cookie来保持登录状态是一种常见的会话管理方式&#xff0c;它通过在客户端存储用户信息来实现用户的…

使用 Python 快速完成管理系统开发:详细教程

如何使用 Python 快速完成管理系统开发&#xff1a;详细教程 Python 是一门功能强大且易于学习的编程语言&#xff0c;广泛应用于各种开发任务&#xff0c;包括管理系统开发。本文将详细介绍如何使用 Python 快速完成一个简单的管理系统开发&#xff0c;涵盖环境搭建、数据库设…