【UE4 设计模式】原型模式 Prototype Pattern
概述
描述
使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。如孙悟空猴毛分身、鸣人影之分身、剑光分化、无限剑制
原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节。
原型模式工作原理是:通过将一个原型对象传给那个要创建的对象,这个要创建的对象通过请求原型对象拷贝它们自己来实施创建对象,即对象Clone()

浅拷贝概念
- 对象语句类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值直接复制一份给新对象。
- 对于数据类型是引用类型的成员变量,那么浅拷贝会进行引用传递,也就是只会讲改成员变量的引用值(内存地址)复制一份给新的对象,实际上两个对象的该成员变量都指向同一个实例,在这种情况下修改一个对象的变量会影响到另一个对象的该成员变量的值。
深拷贝概念
- 复制对象所有基本数据类型的成员变量值
- 为所有引用数据类型的成员白能量申请存储空间,并赋值每个引用数据类型成员变量所引用的对象,也就是说对象进行深拷贝要对整个对象进行拷贝。
- 深拷贝的两种实现方式,1.重写clone方法实现深拷贝,2.通过对象序列化和反序列化实现深拷贝(推荐)
套路
- Prototype(抽象原型类)
它是声明克隆方法的接口,是所有具体原型类的公共父类,可以是抽象类也可以是接口,甚至还可以是具体实现类。 - ConcretePrototype(具体原型类)
它实现在抽象原型类中声明的克隆方法,在克隆方法中返回自己的一个克隆对象 - Client(客户类):让一个原型对象克隆自身从而创建一个新的对象,在客户类中只需要直接实例化或通过工厂方法等方式创建一个原型对象,再通过调用该对象的克隆方法即可得到多个相同的对象。
使用场景
- 有性能要求的场景,如资源优化
- 类初始化需要消耗过多的资源、如数据、硬盘、cpu、网络资源等等
- 实例化新对象需要繁琐的准备步骤
- 需要方便复制的时候,如拖动预设到场景中、场景中物体快速复制
优缺点
- 优点
- 由于客户类针对抽象原型类Prototype编程,因此用户可以根据需要选择具体原型类,系统具有较好的可扩展性,增加或更换具体原型类都很方便。
- 当创建新的对象实例较为复杂时,使用原型模式可以简化对象的创建过程,通过复制一个已有的实例可以提高新实例的创建效率。
- 可以使用深复制的方式保存对象的状态。将对象复制一份并将其状态保存起来,以便于在使用的时候使用,比如恢复到某一个历史状态,可以辅助实现撤销操作。
- 缺点
- 需要为每一个类配备一个克隆方法,而且该克隆方法位于一个类的内部,当对已有的类进行改造时,需要修改源代码,违背了开闭原则。
- 为了支持深复制,当对象之间存在多重嵌套引用关系时,每一层对象都必须支持深复制,实现起来可能比较麻烦。
UE4 实践
写一个角色生成器、生成克隆角色
原型抽象类、具体类 —— 怪物、Ghost、Devil
// 抽象原型类 —— 怪物
UCLASS(Abstract)
class DESIGNPATTERNS_API UMonsterPrototype : public UObject
{
GENERATED_BODY()
public:
// 克隆函数
virtual UMonsterPrototype* Clone() {
UE_LOG(LogTemp, Error, TEXT("Please implement this!"));
return nullptr;
} // 展示信息
virtual void ShowInfo() {
UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" %s [Health]%d, [Speed]%d"), *this->GetName() ,m_pHealth, m_pSpeed);
} protected:
int32 m_pHealth = 100;
int32 m_pSpeed = 30;
}; // 具体产原型类 —— Ghost
UCLASS(Blueprintable, BlueprintType)
class DESIGNPATTERNS_API UGhost : public UMonsterPrototype
{
GENERATED_BODY()
public: // 重载克隆函数
virtual UMonsterPrototype* Clone() override {
UGhost* CloneIns = NewObject<UGhost>();
CloneIns->m_pHealth = m_pHealth;
CloneIns->m_pSpeed = m_pSpeed;
return CloneIns;
}
}; // 具体产原型类 —— Devil
UCLASS(Blueprintable, BlueprintType)
class DESIGNPATTERNS_API UDevil : public UMonsterPrototype
{
GENERATED_BODY()
public:
// 初始化数值
UDevil() {
m_pHealth = 120;
m_pSpeed = 20;
} // 重载克隆函数
virtual UMonsterPrototype* Clone() override {
UDevil* CloneIns = NewObject<UDevil>();
CloneIns->m_pHealth = m_pHealth;
CloneIns->m_pSpeed = m_pSpeed;
CloneIns->m_pAttack = m_pAttack;
return CloneIns;
} // 展示信息
virtual void ShowInfo() override {
UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" %s [Health]%d, [Speed]%d, [Attack] %d"), *this->GetName(), m_pHealth, m_pSpeed, m_pAttack);
}
protected:
int32 m_pAttack = 100;
};
创建工厂类 —— 怪物生成器
// 工厂类 —— 怪物生成器
UCLASS(Blueprintable, BlueprintType)
class DESIGNPATTERNS_API UMonsterSpawner : public UObject
{
GENERATED_BODY()
public: // 生成新怪物,使用模板,避免针对每种怪物都要写一遍
template <class T>
T* SpawnMonster() {
return NewObject<T>();
} // 克隆怪物
UMonsterPrototype* SpawnMonster(UMonsterPrototype* pMonsterClass) {
return pMonsterClass->Clone();
}
};
调用测试
// 调用测试用的Actor
UCLASS()
class DESIGNPATTERNS_API AMonsterSpawnerActor : public AActor
{
GENERATED_BODY()
public: void BeginPlay() override { // 创建工厂
UMonsterSpawner* MonsterSpawner = NewObject<UMonsterSpawner>(); // 第一次创建 Ghost
UGhost* Ghost = MonsterSpawner->SpawnMonster<UGhost>();
// 克隆 Ghost
UGhost* Ghost_Copy1 = Cast<UGhost>(MonsterSpawner->SpawnMonster(Ghost));
Ghost->ShowInfo();
Ghost_Copy1->ShowInfo(); // 第一次创建 Devil
UDevil* Devil =MonsterSpawner->SpawnMonster<UDevil>();
// 克隆 Devil
UDevil* Devil_Copy1 = Cast<UDevil>(MonsterSpawner->SpawnMonster(Devil));
Devil->ShowInfo();
Devil_Copy1->ShowInfo();
}
};
调式输出
LogTemp: Warning: UMonsterPrototype::ShowInfo Ghost_0 [Health]100, [Speed]30
LogTemp: Warning: UMonsterPrototype::ShowInfo Ghost_1 [Health]100, [Speed]30
LogTemp: Warning: UDevil::ShowInfo Devil_0 [Health]120, [Speed]20, [Attack] 100
LogTemp: Warning: UDevil::ShowInfo Devil_1 [Health]120, [Speed]20, [Attack] 100
为数据模型构建原型
在数据建模时,同样可以运用原型模式
给对象声明一个prototype属性,该属性指定另外一个对象,如果访问的属性不在此对象内部,就去prototype指定的对象查找
{
"name": "goblin grunt",
"minHealth": 20,
"maxHealth": 30,
"resists": ["cold", "poison"],
"weaknesses": ["fire", "light"]
}
{
"name": "goblin wizard",
"prototype": "goblin grunt",
"spells": ["fire ball", "lighting bolt"]
}
{
"name": "goblin archer",
"prototype": "goblin grunt",
"attacks": ["short bow"]
}
参考
【UE4 设计模式】原型模式 Prototype Pattern的更多相关文章
- C#设计模式——原型模式(Prototype Pattern)
一.概述 在软件开发中,经常会碰上某些对象,其创建的过程比较复杂,而且随着需求的变化,其创建过程也会发生剧烈的变化,但他们的接口却能比较稳定.对这类对象的创建,我们应该遵循依赖倒置原则,即抽象不应该依 ...
- 设计模式——原型模式(Prototype Pattern)
原型模式:用原型实例制定创建对象的种类,并且通过拷贝这些原型创建新的对象. UML 图: 原型类: package com.cnblog.clarck; /** * 原型类 * * @author c ...
- Net设计模式实例之原型模式( Prototype Pattern)
一.原型模式简介(Brief Introduction) 原型模式(Prototype Pattern):用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象. Specify the kin ...
- 乐在其中设计模式(C#) - 原型模式(Prototype Pattern)
原文:乐在其中设计模式(C#) - 原型模式(Prototype Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 原型模式(Prototype Pattern) 作者:weba ...
- 设计模式系列之原型模式(Prototype Pattern)——对象的克隆
说明:设计模式系列文章是读刘伟所著<设计模式的艺术之道(软件开发人员内功修炼之道)>一书的阅读笔记.个人感觉这本书讲的不错,有兴趣推荐读一读.详细内容也可以看看此书作者的博客https:/ ...
- 二十四种设计模式:原型模式(Prototype Pattern)
原型模式(Prototype Pattern) 介绍用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对象.示例有一个Message实体类,现在要克隆它. MessageModel usin ...
- PHP设计模式 原型模式(Prototype)
定义 和工厂模式类似,用来创建对象.但实现机制不同,原型模式是先创建一个对象,采用clone的方式进行新对象的创建. 场景 大对象的创建. 优点 1.可以在运行时刻增加和删除产品 2.可以改变值或结构 ...
- 2.6 《硬啃设计模式》第8章 复制不是很难 - 原型模式(Prototype Pattern)
案例: 某即时战略游戏,你训练出来各种很强的战士. 为了增加游戏的可玩性,增加了一种复制魔法.实施该魔法,可以复制任意的战士. 你会怎样考虑这个设计? 在继续阅读之前,请先认真思考并写出你的设计,这样 ...
- python 设计模式之原型模式 Prototype Pattern
#引入 例子1: 孙悟空拔下一嘬猴毛,轻轻一吹就会变出好多的孙悟空来. 例子2:寄个快递下面是一个邮寄快递的场景:“给我寄个快递.”顾客说.“寄往什么地方?寄给……?”你问.“和上次差不多一样,只是邮 ...
随机推荐
- Linux复习笔记-001-进程的管理
1.什么是进程? 进程是已经启动的可执行的程序运行实例. 程序是二进制文件,静态 ./bin/date/ /usr/sbin/ 进程:是程序运行的过程 2.Linux为1的进程? centos5或6为 ...
- C++11多线程编程
1. 多线程编程 在进行桌面应用程序开发的时候, 假设应用程序在某些情况下需要处理比较复杂的逻辑, 如果只有一个线程去处理,就会导致窗口卡顿,无法处理用户的相关操作.这种情况下就需要使用多线程,其中一 ...
- GDAL 矢量裁剪栅格
本节将介绍如何在Python中用GDAL实现根据矢量边界裁剪栅格数据. from osgeo import gdal, gdal_array import shapefile import numpy ...
- go语言游戏服务端开发(一)——架构
五邑隐侠,本名关健昌,12年游戏生涯. 本教程以Go语言为例. 网络游戏程序分为客户端和服务端.客户端负责图形渲染.交互和一些简单校验处理,服务端负责业务逻辑处理.数据存储. 我们开发一个游戏de ...
- EXCEL中给包含某个字段的单元格所在行标注颜色
条件格式->新建规则->使用公式确定要设置格式的单元格 公式: =COUNTIF($A1,"*字符串*") 注意:通配符的使用.
- django框架开发流程
python开发没有按目录划分,不像其它语言要先建一个包文件,所以python有必要先新建一个虚拟环境.这样不同的项目所依赖的环境和插件互不影响.虚拟环境的方法很多,这儿先用 virtualenv ...
- Php实现简易购物商城系统
实现功能: 1.系统功能模块包括: 1)登陆注册模块 包括验证码.找回密码.注册模块中要使用Ajax判断用户名是否已经存在,使用正则表达式判断电子邮件.手机号和用户密码的格式是否合法. 2)用户管理模 ...
- 5.21学习总结——android开发实现用户头像的上传
最近在做个人头像的上传,具体是能调用摄像头和从相册进行选择.本篇文章参考的我的同学的博客,大家有兴趣可以去原作者那里去看看: Hi(.・∀・)ノ (cnblogs.com) 1.使用glide进行图片 ...
- 鸿蒙内核源码分析(构建工具篇) | 顺瓜摸藤调试鸿蒙构建过程 | 百篇博客分析OpenHarmony源码 | v59.01
百篇博客系列篇.本篇为: v59.xx 鸿蒙内核源码分析(构建工具篇) | 顺瓜摸藤调试鸿蒙构建过程 | 51.c.h.o 编译构建相关篇为: v50.xx 鸿蒙内核源码分析(编译环境篇) | 编译鸿 ...
- 鸿蒙内核源码分析(任务切换篇) | 看汇编如何切换任务 | 百篇博客分析OpenHarmony源码 | v41.03
百篇博客系列篇.本篇为: v41.xx 鸿蒙内核源码分析(任务切换篇) | 看汇编如何切换任务 | 51.c.h .o 任务管理相关篇为: v03.xx 鸿蒙内核源码分析(时钟任务篇) | 触发调度谁 ...