概述
描述
-
策略模式定義了一系列的算法,並將每一個算法封裝起來,而且使它們還可以相互替換。策略模式讓算法的變化不會影響到使用算法的客戶。
套路
- 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<UWarrior>(); // 使用策略 —— 使用武器 Warrior->UseWeapon(); // 更換策略 —— 更換武器 Warrior->ChangeWeapon(NewObject<UMachete>()); Warrior->UseWeapon(); // 更換策略,不使用武器 Warrior->ChangeWeapon(NewObject<UNoWapon>()); Warrior->UseWeapon(); UE_LOG(LogTemp, Warning, TEXT("---------------------------------------")); // 創建弓箭手 UArcher* Archer = NewObject<UArcher>(); // 使用策略 —— 使用武器 Archer->UseWeapon(); // 更換策略,不使用武器 Archer->ChangeWeapon(NewObject<UNoWapon>()); Archer->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 大刀已裝備