【UE4 设计模式】装饰器模式 Decorator Pattern

时间:2023-03-10 01:41:16
【UE4 设计模式】装饰器模式 Decorator Pattern

概述

描述

  • 动态地给一个对象增加一些额外的职责(Responsibility),就增加对象功能来说,装饰模式比生成子类实现更为灵活。是一种对象结构型模式。

    【UE4 设计模式】装饰器模式 Decorator Pattern

套路

  • 抽象构件(Component)

    具体构件和抽象装饰类的基类,声明了在具体构建中实现的业务方法。
  • 具体构件(ConcreteComponent)

    抽象构件的子类,用于定义具体的构件对象,实现了在抽象构件中声明的方法,装饰器可以给它增加额外的职责(方法)。
  • 抽象装饰类(Decorator)

    它也是抽象构件类的子类,用于给具体构件增加职责,但是具体职责在其子类中实现。
  • 具体装饰类(ConcreteDecorator)

    抽象装饰类的子类,负责向构件添加新的职责。

使用场景

  • 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
  • 需要动态地给一个对象增加功能,这些功能也可以动态地被撤销。
  • 当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时。不能采用继承的情况主要有两类:第一类是系统中存在大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长;第二类是因为类定义不能继承(如final类).
  • 示例
    • 一些api的参数请求追加签名验证
    • 数据的增删修改,会包含登录验证的功能、然后再进行对应的操作;
    • 原来单一的日志记录,追加上传、归档等功能
    • 已经设计好的游戏物品,因为临时需求想要扩展额外的功能

优缺点

  • 优点
    • 对于扩展一个对象的功能,装饰模式比继承更加灵活
    • 可以对一个对象进行多次装饰,从而创造出很多不同行为的组合
    • 具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开闭原则”
  • 缺点
    • 使用装饰模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或者属性值有所不同,同时还将产生很多具体装饰类。这些装饰类和小对象的产生将增加系统的复杂度,加大学习与理解的难度。
    • 这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。

UE4 实践

  • 以前进行账户登录都是直接账户密码就可以,后来为了更安全,更好的采集用户的数据,于是都喜欢加上人脸识别了。这时,可以把人脸识别这一步用装饰器来实现

  • 创建构件抽象类、具体类

    UCLASS(Abstract) // 抽象构件类 —— 账户登录
    class DESIGNPATTERNS_API ULogin : public UObject
    {
    GENERATED_BODY() protected: public:
    virtual bool LoginVerify(const FString& pName, const FString& pPassword) {
    return true;
    }
    }; UCLASS() // 具体构件类 —— 账户登录 联网版本
    class DESIGNPATTERNS_API UWebLogin : public ULogin
    {
    GENERATED_BODY() protected: public:
    virtual bool LoginVerify(const FString& pName, const FString& pPassword) override {
    bool result;
    UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" LinkWeb:Account ID Verify"));
    result = true;
    return result;
    }
    };
  • 创建装饰具体类(省略抽象类)

    UCLASS() // 装饰器具体类 —— 扩展人脸识别步骤
    class DESIGNPATTERNS_API UWebLoginWithFaceID : public ULogin
    {
    GENERATED_BODY() protected:
    // 被装饰的对象
    UPROPERTY();
    ULogin* m_pLoginObj;
    public:
    // 初始化被装饰的对象
    void Init(ULogin* pLoginObj) {
    m_pLoginObj = pLoginObj;
    } // 装饰的新功能 —— 人脸识别
    bool FaceIDVerify(const FString& pName) {
    UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" %s Face ID Verify"), *pName);
    return true;
    } virtual bool LoginVerify(const FString& pName, const FString& pPassword) override {
    bool result;
    result = m_pLoginObj->LoginVerify(pName, pPassword);
    // 追加人脸识别步骤
    result = result && FaceIDVerify(pName);
    return result;
    }
    };
  • 调用测试

    UCLASS()
    class DESIGNPATTERNS_API ADecoratorTestActor : public AActor
    {
    GENERATED_BODY() public:
    ADecoratorTestActor();
    virtual void Tick(float DeltaTime) override; protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override {
    // 被装饰对象及登录步骤
    ULogin* WebLogin = NewObject<UWebLogin>();
    WebLogin->LoginVerify("admin", "123456"); // 装饰器对象及初始化
    UWebLoginWithFaceID* WebLoginWithFaceID = NewObject<UWebLoginWithFaceID>();
    WebLoginWithFaceID->Init(WebLogin); // 装饰后的登录步骤
    WebLoginWithFaceID->LoginVerify("admin", "123456");
    }
    };
  • 调式输出

    # 旧登录方式
    LogTemp: Warning: UWebLogin::LoginVerify LinkWeb:Account ID Verify # 新登录方式
    LogTemp: Warning: UWebLogin::LoginVerify LinkWeb:Account ID Verify
    LogTemp: Warning: UWebLoginWithFaceID::FaceIDVerify admin Face ID Verify

参考