【UE4 设计模式】策略模式 Strategy Pattern
阅读原文时间:2023年07月10日阅读:1

概述

  • 策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法的变化不会影响到使用算法的客户。

  • Context(环境类)

    负责使用算法策略,其中维持了一个抽象策略类的引用实例。

  • Strategy(抽象策略类)

    所有策略类的父类,为所支持的策略算法声明了抽象方法。它既可以是抽象类也可以是接口

  • ConcreteStrategy(具体策略类)

    实现了在抽象策略类中声明的方法。

  • 策略模式可以和简单工厂模式搭配使用

  • 如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。

  • 一个系统需要动态地在几种算法中选择一种。

  • 如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。

  • 不希望客户端知道复杂的、与算法相关的数据结构,在具体策略类中封装算法和相关的数据结构,提高算法的保密性与安全性。

  • 示例

    • 电影出票多种折扣方式、Steam 游戏多种折扣方式,都可以当成多种折扣策略;
    • 多种登录方式(微信登陆、手机号码登录、邮箱登录、扫码登陆),可以视为多种登录策略
    • 多种支付方式(微信支付、支付宝、ApplePay、银联支付),可以视为多种支付策略
    • 不同英雄、武器的伤害计算方式,可以当作多种伤害计算策略
    • 不同职业(种族),使用不同的武器
  • 优点

    • 提供了对开闭原则的完美支持,用户可以在不修改原有系统的基础上选择具体算法或行为,也可以灵活地增加新的算法或行为。
    • 避免了多重的if-else条件选择语句,利于系统的维护。
    • 提供了一种算法的复用机制,不同的环境类可以方便地复用这些策略类。
  • 缺点

    • 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。
    • 策略模式将造成产生很多策略类,可以通过使用享元模式在一定程度上减少对象的数量。

UE4 实践

  • 实现不同职业(种族),使用不同的武器

    • 假定我们有多个职业和多种武器:战士(刀、剑)、法师(短魔杖、长法杖)、弓箭手(长弓、连弩)
    • 想想怎么实现角色使用武器,武器采用抽象继承或接口的方式,似乎有意无意,都会有策略模式的影子
    • 如果后面还要追加角色使用棍、棒、斧、钺、枪呢
  • 创建策略抽象类、具体类 —— 武器,剑、刀、长弓、无武器

    UCLASS(Abstract) // 抽象策略类 —— 武器
    class DESIGNPATTERNS_API UWeapon : public UObject
    {
        GENERATED_BODY()
    public:
        virtual void UseWeapon() {}
    };
    
    UCLASS() // 具体策略类 —— 剑
    class DESIGNPATTERNS_API USword : public UWeapon
    {
        GENERATED_BODY()
    public:
        virtual void UseWeapon() override {
            UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" 长剑已装备"));
        }
    };
    
    UCLASS() // 具体策略类 —— 刀
    class DESIGNPATTERNS_API UMachete : public UWeapon
    {
        GENERATED_BODY()
    public:
        virtual void UseWeapon() override {
            UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" 大刀已装备"));
        }
    };
    
    UCLASS() // 具体策略类 —— 无武器
    class DESIGNPATTERNS_API UNoWapon : public UWeapon
    {
        GENERATED_BODY()
    public:
        virtual void UseWeapon() override {
            UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" 赤手空拳"));
        }
    };
    
    UCLASS() // 具体策略类 —— 长弓
    class DESIGNPATTERNS_API ULongbow : public UWeapon
    {
        GENERATED_BODY()
    public:
        virtual void UseWeapon() override {
            UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" 长弓已装备"));
        }
    };
  • 创建环境类,采用继承的方式创建不同的角色类,此处有战士和弓箭手

    UCLASS(Abstract) // 环境抽象类 —— 角色
    class DESIGNPATTERNS_API UHero : public UObject
    {
        GENERATED_BODY()
    public:
        // 使用策略 —— 使用武器
        virtual void UseWeapon() {
            if (m_pWeapon)
            {
                m_pWeapon->UseWeapon();
            }
        }
        // 更换策略 —— 更换武器
        virtual void ChangeWeapon(UWeapon* pWeapon ) {
            m_pWeapon = pWeapon;
            UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" 武器已更换"));
        }
    
    protected:
        UPROPERTY()
        UWeapon* m_pWeapon;
    };
    
    UCLASS() // 环境类 —— 战士
    class DESIGNPATTERNS_API UWarrior : public UHero
    {
        GENERATED_BODY()
    public:
        UWarrior() { // 为不同角色初始化武器
            m_pWeapon = NewObject<USword>();
            UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" 这是一个战士"));
        }
    };
    
    UCLASS() // 环境类 —— 弓箭手
    class DESIGNPATTERNS_API UArcher : public UHero
    {
        GENERATED_BODY()
    public:
        UArcher() { // 为不同角色初始化武器
            m_pWeapon = NewObject<ULongbow>();
            UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" 这是一个弓箭手"));
        }
    };
  • 调用测试

    UCLASS()
    class DESIGNPATTERNS_API AStrategyTestActor : public AActor
    {
        GENERATED_BODY()
    
    public:
        // Sets default values for this actor's properties
        AStrategyTestActor();
    
    protected:
        // Called when the game starts or when spawned
        virtual void BeginPlay() override {
        // 创建战士
        UWarrior* Warrior = NewObject&lt;UWarrior&gt;();
        // 使用策略 —— 使用武器
        Warrior-&gt;UseWeapon();
    
        // 更换策略 —— 更换武器
        Warrior-&gt;ChangeWeapon(NewObject&lt;UMachete&gt;());
        Warrior-&gt;UseWeapon();
    
        // 更换策略,不使用武器
        Warrior-&gt;ChangeWeapon(NewObject&lt;UNoWapon&gt;());
        Warrior-&gt;UseWeapon();
    
        UE_LOG(LogTemp, Warning, TEXT("---------------------------------------"));
        // 创建弓箭手
        UArcher* Archer = NewObject&lt;UArcher&gt;();
        // 使用策略 —— 使用武器
        Archer-&gt;UseWeapon();
    
        // 更换策略,不使用武器
        Archer-&gt;ChangeWeapon(NewObject&lt;UNoWapon&gt;());
        Archer-&gt;UseWeapon();
    }
    };
  • 调式输出

    LogTemp: Warning: UWarrior::UWarrior 这是一个战士
    LogTemp: Warning: USword::UseWeapon 长剑已装备
    LogTemp: Warning: UHero::ChangeWeapon 武器已更换
    LogTemp: Warning: UMachete::UseWeapon 大刀已装备
    LogTemp: Warning: UHero::ChangeWeapon 武器已更换
    LogTemp: Warning: UMachete::UseWeapon 大刀已装备
    LogTemp: Warning: ---------------------------------------
    LogTemp: Warning: UArcher::UArcher 这是一个弓箭手
    LogTemp: Warning: ULongbow::UseWeapon 长弓已装备
    LogTemp: Warning: UHero::ChangeWeapon 武器已更换
    LogTemp: Warning: UMachete::UseWeapon 大刀已装备

参考