2.策略模式

2.行为型模式-策略模式


2.1 基础知识

学习难度:1

使用频率:4

总分:9

定义

策略模式(Strategy Pattern)定义了算法家族,分别封装起来,让它们之间可以互相替换。此模式让算法的变化不会影响到使用算法的客户端。

说人话

封装不同的策略让上下文进行管理,实现切换策略的使用。

结构图

实现步骤

  • 策略接口:定义执行策略方法
  • 多个具体策略类:实现策略接口
  • 上下文类:定义策略对象,定义外部传入设置策略对象的方法,定义执行策略方法,方法中只要策略对象不为空就调用策略对象的执行策略方法
  • 使用时:实例化多个具体策略,实例化上下文并传入某具体策略,调用上下文的执行策略方法,可以对上下文重新设置不同的具体策略并重新执行策略方法。

说明

策略模式可以有效地减少大量的条件语句,使代码更加清晰,易于维护。适用于需要在运行时动态地选择算法的情况。


2.2 模版代码

策略接口和具体策略类

// 定义策略接口
public interface IStrategy
{
    void Execute();
}

// 具体策略类1
public class ConcreteStrategyA : IStrategy
{
    public void Execute()
    {
        Console.WriteLine("执行策略A");
        // 在这里实现策略A的具体逻辑
    }
}

// 具体策略类2
public class ConcreteStrategyB : IStrategy
{
    public void Execute()
    {
        Console.WriteLine("执行策略B");
        // 在这里实现策略B的具体逻辑
    }
}

上下文类

// 上下文类,用于设置和切换策略
public class Context
{
    private IStrategy strategy;

    // 设置策略
    public void SetStrategy(IStrategy strategy)
    {
        this.strategy = strategy;
    }

    // 执行策略
    public void ExecuteStrategy()
    {
        if (strategy != null)
        {
            strategy.Execute();
        }
        else
        {
            Console.WriteLine("未设置策略");
        }
    }
}

客户端

class Program
{
    static void Main(string[] args)
    {
        // 创建上下文对象
        Context context = new Context();

        // 设置策略A
        context.SetStrategy(new ConcreteStrategyA());

        // 执行策略A
        context.ExecuteStrategy();

        // 切换到策略B
        context.SetStrategy(new ConcreteStrategyB());

        // 执行策略B
        context.ExecuteStrategy();
    }
}

2.3 CSharp实践

实践需求

使用策略模式,模拟商场优惠策略需求。分别有正常购买策略,打折策略和满减策略。

购买策略接口和具体购买策略类

// 购买策略接口
public interface ICashStrategy
{
    double CalculateFinalPrice(double originalPrice);
}

// 正常购买策略
public class CashNormal : ICashStrategy
{
    public double CalculateFinalPrice(double originalPrice)
    {
        return originalPrice;
    }
}

// 打折购买策略
public class CashRebate : ICashStrategy
{
    private double discountRate; // 折扣率

    public CashRebate(double discountRate)
    {
        this.discountRate = discountRate;
    }

    public double CalculateFinalPrice(double originalPrice)
    {
        return originalPrice * discountRate;
    }
}

// 满减返现购买策略
public class CashReturn : ICashStrategy
{
    private double cashReduceThreshold; // 满减门槛金额
    private double cashback;  // 返现金额

    public CashReturn(double cashReduceThreshold, double cashback)
    {
        this.cashReduceThreshold = cashReduceThreshold;
        this.cashback = cashback;
    }

    public double CalculateFinalPrice(double originalPrice)
    {
        if (originalPrice >= cashReduceThreshold)
        {
            return originalPrice - cashback;
        }
        return originalPrice;
    }
}

购买上下文类

// 上下文类,用于设置和切换购买策略
public class CashContext
{
    private ICashStrategy cashStrategy; // 持有购买策略对象

    // 设置购买策略的方法
    public void SetCashStrategy(ICashStrategy strategy)
    {
        cashStrategy = strategy;
    }

    public double CalculateFinalPrice(double originalPrice)
    {
        if (cashStrategy != null)
        {
            return cashStrategy.CalculateFinalPrice(originalPrice); // 调用购买策略对象的计算方法
        }
        return originalPrice;
    }
}

客户端

class Program
{
    static void Main(string[] args)
    {
        // 创建 CashContext 对象
        CashContext cashContext = new CashContext();

        // 默认使用正常购买策略
        double originalPrice = 350.0;
        double finalPrice = cashContext.CalculateFinalPrice(originalPrice);
        Console.WriteLine($"原价:${originalPrice}, 最终价格:${finalPrice}");
        // 原价:$350, 最终价格:$350
        
        // 切换到打折购买策略
        cashContext.SetCashStrategy(new CashRebate(0.8));
        finalPrice = cashContext.CalculateFinalPrice(originalPrice);
        Console.WriteLine($"原价:${originalPrice}, 最终价格:${finalPrice}");
        // 原价:$350, 最终价格:$280

        // 切换到满减返现购买策略
        cashContext.SetCashStrategy(new CashReturn(300, 100));
        finalPrice = cashContext.CalculateFinalPrice(originalPrice);
        Console.WriteLine($"原价:${originalPrice}, 最终价格:${finalPrice}");
        // 原价:$350, 最终价格:$250
    }
}

2.4 Unity实践

实践需求

使用策略模式,实现按下AD键或←→键切换显示的英雄。

显示英雄策略接口和具体显示英雄策略类

//显示英雄策略
public interface IShowHeroStrategy
{
    GameObject ShowHero();
}

//显示薇恩策略
public class ShowVayneStrategy : IShowHeroStrategy
{
    public GameObject ShowHero()
    {
        GameObject hero = Resources.Load<GameObject>("Lesson20_行为型模式_策略模式/Vayne");
        return GameObject.Instantiate(hero, Vector3.zero, Quaternion.identity);
    }
}

//显示剑圣策略
public class ShowYiStrategy : IShowHeroStrategy
{
    public GameObject ShowHero()
    {
        GameObject hero = Resources.Load<GameObject>("Lesson20_行为型模式_策略模式/Yi");
        return GameObject.Instantiate(hero, Vector3.zero, Quaternion.identity);
    }
}

显示英雄上下文

//显示英雄上下文
public class ShowHeroContext
{
    private IShowHeroStrategy showHeroStrategy;

    public void SetShowHeroContext(IShowHeroStrategy showHeroStrategy)
    {
        this.showHeroStrategy = showHeroStrategy;
    }

    public GameObject ExecuteStrategy()
    {
        return showHeroStrategy.ShowHero();
    }
}

客户端

public class TestStrategyPattern : MonoBehaviour
{
    private ShowHeroContext showHeroContext; // 用于管理英雄策略的上下文
    private List<Type> showHeroStrategyTypeList = new List<Type>(); // 存储可用的英雄策略类型
    private int showHeroStrategyTypeListIndex; // 当前选择的英雄策略类型的索引
    private int ShowHeroStrategyTypeListIndex
    {
        get => showHeroStrategyTypeListIndex;
        set => showHeroStrategyTypeListIndex = (value + showHeroStrategyTypeList.Count) % showHeroStrategyTypeList.Count;
        // 设置当前英雄策略类型的索引,并确保循环在列表范围内
    }

    private GameObject nowHeroGameObject; // 当前英雄的游戏对象
    private float changeHeroCD = 0.5f; // 切换英雄的冷却时间

    private void Start()
    {
        // 向列表中添加不同的英雄策略类型
        showHeroStrategyTypeList.Add(typeof(ShowVayneStrategy)); 
        showHeroStrategyTypeList.Add(typeof(ShowYiStrategy));

        showHeroContext = new ShowHeroContext(); // 初始化英雄策略上下文
        ChangeHero(0);
    }

    private void Update()
    {
        if (changeHeroCD > 0)
        {
            changeHeroCD -= Time.deltaTime;
            // 更新切换英雄的冷却时间
        }

        float horizontalInput = Input.GetAxis("Horizontal"); // 获取水平输入值

        if (Mathf.Abs(horizontalInput) > 0.1f && changeHeroCD <= 0)
        {
            // 如果水平输入的绝对值大于0.1且冷却时间已过,执行切换英雄策略
            int direction = (horizontalInput > 0) ? 1 : -1; // 根据水平输入的正负来确定切换的方向
            ChangeHero(direction);
        }
    }

    private void ChangeHero(int direction)
    {
        changeHeroCD = 0.5f; // 重置切换英雄的冷却时间

        ShowHeroStrategyTypeListIndex += direction; // 根据方向增加或减少英雄策略类型的索引,确保在列表范围内循环

        Type nowHeroStrategyType = showHeroStrategyTypeList[ShowHeroStrategyTypeListIndex]; // 获取当前的英雄策略类型
        showHeroContext.SetShowHeroContext(Activator.CreateInstance(nowHeroStrategyType) as IShowHeroStrategy); // 设置新的英雄策略

        Destroy(nowHeroGameObject); // 销毁当前的英雄游戏对象

        nowHeroGameObject = showHeroContext.ExecuteStrategy(); // 执行新的英雄策略,并保存返回的英雄游戏对象
    }
}

运行结果




转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏