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