• UE4 C++设计模式:原型模式(Prototype Pattern)


    目录

    描述

    套路

    使用场景

    优缺点

    UE4实践

    原型抽象类-怪物原型

    具体类-怪物(Ghost、Devil)

    创建工厂类-怪物生成器


    描述

    • 使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。比如分身
    • 原型模式是一种创建型设计模式,允许一个对象再创建另一个可定制的对象,无需知道如何创建的细节
    • 原型模式工作原理:通过将一个原型对象传给要创建的对象,这个要创建的对象通过请求原型对象拷贝他们自己来实施创建对象,即对象clone()

    • 浅拷贝
      • 对象语句类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性直接复制一份给新对象
      • 对于数据类型是引用类型的成员变量,浅拷贝会进行引用传递,也就是改变成员变量的引用值(内存地址)复制一份给新的对象,实际上两个对象的该成员变量指向同一个实例,在这种情况下修改一个对象的变量会影响到另一个对象的该成员变量的值
    • 深拷贝
      • 复制对象的所有基本数据类型的成员变量值
      • 为所有引用数据类型的成员申请存储空间
      • 实现方式:重写clone方法、对象序列化和反序列化(推荐)

    套路

    • Prototype(抽象原型类)
      • 声明clone方法的接口,是所有具体原型的公共父类,可以是抽象类也可以是接口,甚至也可以是具体实现类
    • ConcreteProtoType(具体原型类)
      • 它实现在抽象原型类中的clone方法,在clone方法中返回自己的一个克隆对象
    • Client(客户类)
      • 让一个原型对象clone自身从而创建一个新的对象,在客户类中只需要直接实例化或通过工厂方法等方式创建一个原型对象,再通过调用该对象的clone方法即可得到多个相同对象

    使用场景

    • 有性能需求的场景,如资源优化
    • 类初始化需要消耗过多的资源,如数据、硬盘、CPU、网络资源等
    • 实例化新对象需要繁琐的准备步骤
    • 需要方便复制的时候,如拖动预设到场景中、场景中物体快速赋值

    优缺点

    • 优点
      • 由于客户类针对抽象原型类Prototype编程,因此用户可以根据需要选择具体原型类,系统具有良好的可扩展性,增加或更换具体原型类都很方便
      • 当创建新的对象实例较为复杂时,将使用原型模式可以简化对象的创建过程,通过赋值一个已有实例可以提高新实例的创建效率
      • 可以使用深拷贝的方式保存对象的状态,将对象复制一份并将其状态保存起来,以便于在使用的时候使用,比如恢复到某一个历史状态,可以辅助实现撤销操作
    • 缺点
      • 需要为每一个类配备一个clone方法,而且该clone方法位于一个类的内部,当对已有类进行改造时,需要修改源代码,违背了开闭原则
      • 为了支持深拷贝,当对象之间存在多重嵌套关系,每一层对象都必须支持深拷贝,实现起来比较麻烦

    UE4实践

    角色生成器,生成克隆角色

    原型抽象类-怪物原型

    1. // 抽象原型类 —— 怪物
    2. UCLASS(Abstract)
    3. class DESIGNPATTERNS_API UMonsterPrototype : public UObject
    4. {
    5. GENERATED_BODY()
    6. public:
    7. // 克隆函数
    8. virtual UMonsterPrototype* Clone() {
    9. UE_LOG(LogTemp, Error, TEXT("Please implement this!"));
    10. return nullptr;
    11. }
    12. // 展示信息
    13. virtual void ShowInfo() {
    14. UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" %s [Health]%d, [Speed]%d"), *this->GetName() ,m_pHealth, m_pSpeed);
    15. }
    16. protected:
    17. int32 m_pHealth = 100;
    18. int32 m_pSpeed = 30;
    19. };

    具体类-怪物(Ghost、Devil)

    1. // 具体产原型类 —— Ghost
    2. UCLASS(Blueprintable, BlueprintType)
    3. class DESIGNPATTERNS_API UGhost : public UMonsterPrototype
    4. {
    5. GENERATED_BODY()
    6. public:
    7. // 重载克隆函数
    8. virtual UMonsterPrototype* Clone() override {
    9. UGhost* CloneIns = NewObject();
    10. CloneIns->m_pHealth = m_pHealth;
    11. CloneIns->m_pSpeed = m_pSpeed;
    12. return CloneIns;
    13. }
    14. };
    15. // 具体产原型类 —— Devil
    16. UCLASS(Blueprintable, BlueprintType)
    17. class DESIGNPATTERNS_API UDevil : public UMonsterPrototype
    18. {
    19. GENERATED_BODY()
    20. public:
    21. // 初始化数值
    22. UDevil() {
    23. m_pHealth = 120;
    24. m_pSpeed = 20;
    25. }
    26. // 重载克隆函数
    27. virtual UMonsterPrototype* Clone() override {
    28. UDevil* CloneIns = NewObject();
    29. CloneIns->m_pHealth = m_pHealth;
    30. CloneIns->m_pSpeed = m_pSpeed;
    31. CloneIns->m_pAttack = m_pAttack;
    32. return CloneIns;
    33. }
    34. // 展示信息
    35. virtual void ShowInfo() override {
    36. UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" %s [Health]%d, [Speed]%d, [Attack] %d"), *this->GetName(), m_pHealth, m_pSpeed, m_pAttack);
    37. }
    38. protected:
    39. int32 m_pAttack = 100;
    40. };

    创建工厂类-怪物生成器

    1. // 工厂类 —— 怪物生成器
    2. UCLASS(Blueprintable, BlueprintType)
    3. class DESIGNPATTERNS_API UMonsterSpawner : public UObject
    4. {
    5. GENERATED_BODY()
    6. public:
    7. // 生成新怪物,使用模板,避免针对每种怪物都要写一遍
    8. template <class T>
    9. T* SpawnMonster() {
    10. return NewObject();
    11. }
    12. // 克隆怪物
    13. UMonsterPrototype* SpawnMonster(UMonsterPrototype* pMonsterClass) {
    14. return pMonsterClass->Clone();
    15. }
    16. };

    测试

    1. // 调用测试用的Actor
    2. UCLASS()
    3. class DESIGNPATTERNS_API AMonsterSpawnerActor : public AActor
    4. {
    5. GENERATED_BODY()
    6. public:
    7. void BeginPlay() override {
    8. // 创建工厂
    9. UMonsterSpawner* MonsterSpawner = NewObject();
    10. // 第一次创建 Ghost
    11. UGhost* Ghost = MonsterSpawner->SpawnMonster();
    12. // 克隆 Ghost
    13. UGhost* Ghost_Copy1 = Cast(MonsterSpawner->SpawnMonster(Ghost));
    14. Ghost->ShowInfo();
    15. Ghost_Copy1->ShowInfo();
    16. // 第一次创建 Devil
    17. UDevil* Devil =MonsterSpawner->SpawnMonster();
    18. // 克隆 Devil
    19. UDevil* Devil_Copy1 = Cast(MonsterSpawner->SpawnMonster(Devil));
    20. Devil->ShowInfo();
    21. Devil_Copy1->ShowInfo();
    22. }
    23. };
    1. LogTemp: Warning: UMonsterPrototype::ShowInfo Ghost_0 [Health]100, [Speed]30
    2. LogTemp: Warning: UMonsterPrototype::ShowInfo Ghost_1 [Health]100, [Speed]30
    3. LogTemp: Warning: UDevil::ShowInfo Devil_0 [Health]120, [Speed]20, [Attack] 100
    4. LogTemp: Warning: UDevil::ShowInfo Devil_1 [Health]120, [Speed]20, [Attack] 100

  • 相关阅读:
    【muduo源码剖析】EventLoop设计分析
    对比网络层 后意识层 自回归解码 有意识的神经网络
    这些大厂笔试题 你都见识(指被无情鞭挞)过了吗?—— 哔哩哔哩篇
    图像分类(四) 全面解读复现GoogleNet_InceptionV1-V4
    Higress 全新 Wasm 运行时,性能大幅提升
    对比Excel学openpyxl系列之单元格选择与字体设置
    插入排序(直接、二分)
    关于容器镜像那些事
    什么是泛型
    编写一个Book类,需要进行封装,对外提供set和get方法且对页数进行控制
  • 原文地址:https://blog.csdn.net/Jason6620/article/details/126537796