【UE4 设计模式】原型模式 Prototype Pattern

概述

描述

  • 使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。如孙悟空猴毛分身、鸣人影之分身、剑光分化、无限剑制

  • 原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节。

  • 原型模式工作原理是:通过将一个原型对象传给那个要创建的对象,这个要创建的对象通过请求原型对象拷贝它们自己来实施创建对象,即对象Clone()

    【UE4 设计模式】原型模式 Prototype Pattern

  • 浅拷贝概念

    • 对象语句类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值直接复制一份给新对象。
    • 对于数据类型是引用类型的成员变量,那么浅拷贝会进行引用传递,也就是只会讲改成员变量的引用值(内存地址)复制一份给新的对象,实际上两个对象的该成员变量都指向同一个实例,在这种情况下修改一个对象的变量会影响到另一个对象的该成员变量的值。
  • 深拷贝概念

    • 复制对象所有基本数据类型的成员变量值
    • 为所有引用数据类型的成员白能量申请存储空间,并赋值每个引用数据类型成员变量所引用的对象,也就是说对象进行深拷贝要对整个对象进行拷贝。
    • 深拷贝的两种实现方式,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"]
    }

参考

上一篇:2.6 《硬啃设计模式》第8章 复制不是很难 - 原型模式(Prototype Pattern)


下一篇:ASP.NET MVC中给所有的cshtml页面引用命名空间