C# 和Unity 中的策略模式 详解
·
C# 策略模式详解
一、策略模式基本概念
策略模式(Strategy Pattern)是一种行为设计模式,它定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户端。
核心思想
将算法或行为抽象为独立的策略类,使得它们可以独立于客户端而变化。
主要角色
- Context(上下文) :维护一个策略对象的引用,通过策略接口与具体策略交互
- Strategy(策略接口) :定义所有支持的算法或行为的公共接口
- ConcreteStrategy(具体策略) :实现策略接口的具体算法类
二、策略模式特点
优点
- 开闭原则:无需修改上下文就能引入新策略
- 消除条件语句:避免使用大量条件语句来选择算法
- 算法复用:相同策略可被不同上下文使用
- 算法独立:将算法实现与使用它的代码分离
缺点
- 客户端必须了解策略差异:客户端需要知道不同策略的区别以选择合适的策略
- 类数量增加:如果策略很多,会导致类数量膨胀
- 通信开销:策略与上下文之间需要通过接口通信,可能增加开销
三、策略模式实现
1. 基础实现(排序策略示例)
// 策略接口:定义排序算法接口
public interface ISortStrategy
{
void Sort(List<int> dataset);
}
// 具体策略:冒泡排序
public class BubbleSortStrategy : ISortStrategy
{
public void Sort(List<int> dataset)
{
Console.WriteLine("使用冒泡排序算法");
// 实际实现省略...
for (int i = 0; i < dataset.Count; i++)
{
for (int j = 0; j < dataset.Count - i - 1; j++)
{
if (dataset[j] > dataset[j + 1])
{
// 交换元素
int temp = dataset[j];
dataset[j] = dataset[j + 1];
dataset[j + 1] = temp;
}
}
}
}
}
// 具体策略:快速排序
public class QuickSortStrategy : ISortStrategy
{
public void Sort(List<int> dataset)
{
Console.WriteLine("使用快速排序算法");
// 实际实现省略...
QuickSort(dataset, 0, dataset.Count - 1);
}
private void QuickSort(List<int> arr, int left, int right)
{
if (left < right)
{
int pivot = Partition(arr, left, right);
QuickSort(arr, left, pivot - 1);
QuickSort(arr, pivot + 1, right);
}
}
private int Partition(List<int> arr, int left, int right)
{
int pivot = arr[right];
int i = left - 1;
for (int j = left; j < right; j++)
{
if (arr[j] < pivot)
{
i++;
Swap(arr, i, j);
}
}
Swap(arr, i + 1, right);
return i + 1;
}
private void Swap(List<int> arr, int i, int j)
{
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
// 上下文类:使用策略的客户端
public class Sorter
{
private ISortStrategy _strategy;
// 设置策略
public void SetStrategy(ISortStrategy strategy)
{
_strategy = strategy;
}
// 执行排序
public void Sort(List<int> dataset)
{
_strategy?.Sort(dataset);
}
}
// 使用示例
var dataset = new List<int> { 5, 2, 4, 6, 1, 3 };
var sorter = new Sorter();
// 使用冒泡排序
sorter.SetStrategy(new BubbleSortStrategy());
sorter.Sort(dataset);
// 使用快速排序
sorter.SetStrategy(new QuickSortStrategy());
sorter.Sort(dataset);
2. 更复杂实现(支付策略示例)
// 策略接口:定义支付方式接口
public interface IPaymentStrategy
{
void Pay(decimal amount);
}
// 具体策略:信用卡支付
public class CreditCardPayment : IPaymentStrategy
{
private string _cardNumber;
private string _expiryDate;
private string _cvv;
public CreditCardPayment(string cardNumber, string expiryDate, string cvv)
{
_cardNumber = cardNumber;
_expiryDate = expiryDate;
_cvv = cvv;
}
public void Pay(decimal amount)
{
Console.WriteLine($"使用信用卡支付 {amount}元");
Console.WriteLine($"卡号: {_cardNumber}, 有效期: {_expiryDate}, CVV: {_cvv}");
// 实际支付处理逻辑...
}
}
// 具体策略:支付宝支付
public class AlipayPayment : IPaymentStrategy
{
private string _account;
public AlipayPayment(string account)
{
_account = account;
}
public void Pay(decimal amount)
{
Console.WriteLine($"使用支付宝支付 {amount}元");
Console.WriteLine($"支付宝账号: {_account}");
// 实际支付处理逻辑...
}
}
// 具体策略:微信支付
public class WechatPayment : IPaymentStrategy
{
private string _openId;
public WechatPayment(string openId)
{
_openId = openId;
}
public void Pay(decimal amount)
{
Console.WriteLine($"使用微信支付 {amount}元");
Console.WriteLine($"微信OpenID: {_openId}");
// 实际支付处理逻辑...
}
}
// 上下文类:购物车
public class ShoppingCart
{
private IPaymentStrategy _paymentStrategy;
private List<Item> _items = new List<Item>();
// 添加商品
public void AddItem(Item item)
{
_items.Add(item);
}
// 设置支付策略
public void SetPaymentStrategy(IPaymentStrategy strategy)
{
_paymentStrategy = strategy;
}
// 结算
public void Checkout()
{
decimal total = _items.Sum(item => item.Price);
Console.WriteLine($"订单总金额: {total}元");
_paymentStrategy?.Pay(total);
}
}
// 商品类
public class Item
{
public string Name { get; set; }
public decimal Price { get; set; }
}
// 使用示例
var cart = new ShoppingCart();
cart.AddItem(new Item { Name = "商品1", Price = 100 });
cart.AddItem(new Item { Name = "商品2", Price = 200 });
// 选择支付方式
cart.SetPaymentStrategy(new CreditCardPayment("1234-5678-9012-3456", "12/25", "123"));
cart.Checkout();
// 更换支付方式
cart.SetPaymentStrategy(new AlipayPayment("user@example.com"));
cart.Checkout();
四、策略模式使用场景
- 多种算法变体:当系统需要在多种算法中选择一种时
- 消除条件语句:当有多个条件语句决定使用哪种算法变体时
- 算法需要独立变化:当算法需要独立于使用它的客户端变化时
- 运行时选择算法:当需要在运行时选择不同算法时
典型应用场景
- 支付系统:支持多种支付方式(信用卡、支付宝、微信等)
- 排序算法:根据需要选择不同排序算法
- 压缩工具:支持不同压缩算法(ZIP、RAR、7z等)
- 导航系统:提供不同路线策略(最快、最短、最少收费等)
- 游戏AI:为角色提供不同行为策略
五、策略模式进阶技巧
1. 策略工厂模式结合
// 策略工厂类
public class PaymentStrategyFactory
{
private Dictionary<string, Func<IPaymentStrategy>> _strategies;
public PaymentStrategyFactory()
{
_strategies = new Dictionary<string, Func<IPaymentStrategy>>();
// 注册策略创建方法
_strategies["credit"] = () => new CreditCardPayment("1234-5678-9012-3456", "12/25", "123");
_strategies["alipay"] = () => new AlipayPayment("user@example.com");
_strategies["wechat"] = () => new WechatPayment("wx123456789");
}
// 获取策略
public IPaymentStrategy GetStrategy(string paymentType)
{
if (_strategies.TryGetValue(paymentType, out var strategyCreator))
{
return strategyCreator();
}
throw new ArgumentException("不支持的支付方式");
}
}
// 使用工厂创建策略
var factory = new PaymentStrategyFactory();
var strategy = factory.GetStrategy("alipay");
var cart = new ShoppingCart();
cart.SetPaymentStrategy(strategy);
cart.AddItem(new Item { Name = "商品", Price = 300 });
cart.Checkout();
2. Lambda表达式策略
// 使用委托代替策略接口
public class Calculator
{
private Func<int, int, int> _strategy;
public void SetStrategy(Func<int, int, int> strategy)
{
_strategy = strategy;
}
public int Execute(int a, int b)
{
return _strategy?.Invoke(a, b) ?? throw new InvalidOperationException("策略未设置");
}
}
// 使用Lambda表达式作为策略
var calculator = new Calculator();
// 加法策略
calculator.SetStrategy((a, b) => a + b);
Console.WriteLine(calculator.Execute(5, 3)); // 输出8
// 乘法策略
calculator.SetStrategy((a, b) => a * b);
Console.WriteLine(calculator.Execute(5, 3)); // 输出15
3. 策略模式与依赖注入
// 在ASP.NET Core中使用策略模式
public interface IDiscountStrategy
{
decimal ApplyDiscount(decimal originalPrice);
}
public class RegularCustomerDiscount : IDiscountStrategy
{
public decimal ApplyDiscount(decimal originalPrice) => originalPrice * 0.9m;
}
public class VIPCustomerDiscount : IDiscountStrategy
{
public decimal ApplyDiscount(decimal originalPrice) => originalPrice * 0.7m;
}
// 注册服务
services.AddTransient<IDiscountStrategy, RegularCustomerDiscount>();
services.AddTransient<IDiscountStrategy, VIPCustomerDiscount>();
// 使用服务
public class OrderService
{
private readonly IEnumerable<IDiscountStrategy> _discountStrategies;
public OrderService(IEnumerable<IDiscountStrategy> discountStrategies)
{
_discountStrategies = discountStrategies;
}
public decimal ApplyDiscount(string customerType, decimal price)
{
var strategy = _discountStrategies.FirstOrDefault(s =>
(customerType == "VIP" && s is VIPCustomerDiscount) ||
(customerType == "Regular" && s is RegularCustomerDiscount));
return strategy?.ApplyDiscount(price) ?? price;
}
}
六、策略模式最佳实践
- 合理设计策略接口:确保接口足够通用,能覆盖所有策略变体
- 策略无状态化:尽可能设计无状态的策略类,便于共享和复用
- 客户端了解策略:确保客户端代码了解不同策略的区别
- 避免策略膨胀:当策略过多时,考虑使用其他模式(如责任链)
- 组合使用模式:策略模式常与工厂模式、模板方法模式等结合使用
七、策略模式与其他模式的关系
-
与状态模式:
- 策略模式:客户端主动选择策略,策略间通常不相互知晓
- 状态模式:状态自动转换,状态间通常相互知晓
-
与命令模式:
- 策略模式:关注算法/策略的选择
- 命令模式:关注请求的封装和参数化
-
与模板方法模式:
- 策略模式:通过对象组合实现算法变化
- 模板方法模式:通过类继承实现算法部分变化
八、总结
策略模式是处理算法变化的强大工具,它通过以下方式提高代码质量:
- 消除复杂的条件判断
- 将算法实现与使用它的代码分离
- 使系统更容易扩展新算法
- 符合开闭原则
在C#中实现策略模式时,关键点是:
- 明确定义策略接口
- 将可变行为封装到具体策略类中
- 通过组合而非继承来使用策略
- 根据需求选择简单或复杂的实现方式
正确使用策略模式可以使系统更加灵活和可维护,特别适合那些有多种算法变体且需要动态切换的场景
九、在Unity中的应用
示例1:AI行为策略
using UnityEngine;
// 策略接口
public interface IAIBehavior
{
void Execute();
}
// 具体策略:巡逻行为
public class PatrolBehavior : IAIBehavior
{
private Transform enemyTransform;
private float speed;
private Vector3[] waypoints;
private int currentWaypoint = 0;
public PatrolBehavior(Transform transform, float speed, Vector3[] waypoints)
{
this.enemyTransform = transform;
this.speed = speed;
this.waypoints = waypoints;
}
public void Execute()
{
// 巡逻逻辑
if (waypoints.Length == 0) return;
// 移动到当前路点
enemyTransform.position = Vector3.MoveTowards(
enemyTransform.position,
waypoints[currentWaypoint],
speed * Time.deltaTime);
// 检查是否到达路点
if (Vector3.Distance(enemyTransform.position, waypoints[currentWaypoint]) < 0.1f)
{
currentWaypoint = (currentWaypoint + 1) % waypoints.Length;
}
}
}
// 具体策略:追击行为
public class ChaseBehavior : IAIBehavior
{
private Transform enemyTransform;
private Transform playerTransform;
private float speed;
public ChaseBehavior(Transform enemyTransform, Transform playerTransform, float speed)
{
this.enemyTransform = enemyTransform;
this.playerTransform = playerTransform;
this.speed = speed;
}
public void Execute()
{
// 追击逻辑
if (playerTransform != null)
{
Vector3 direction = (playerTransform.position - enemyTransform.position).normalized;
enemyTransform.position += direction * speed * Time.deltaTime;
}
}
}
// 具体策略:逃跑行为
public class FleeBehavior : IAIBehavior
{
private Transform enemyTransform;
private Transform playerTransform;
private float speed;
public FleeBehavior(Transform enemyTransform, Transform playerTransform, float speed)
{
this.enemyTransform = enemyTransform;
this.playerTransform = playerTransform;
this.speed = speed;
}
public void Execute()
{
// 逃跑逻辑
if (playerTransform != null)
{
Vector3 direction = (enemyTransform.position - playerTransform.position).normalized;
enemyTransform.position += direction * speed * Time.deltaTime;
}
}
}
// 上下文类 - 使用策略的AI控制器
public class AIController : MonoBehaviour
{
private IAIBehavior currentBehavior;
public Transform player;
public float speed = 3f;
public Vector3[] patrolWaypoints;
private void Start()
{
// 初始设置为巡逻行为
SetPatrolBehavior();
}
private void Update()
{
// 根据玩家距离切换行为
float distanceToPlayer = Vector3.Distance(transform.position, player.position);
if (distanceToPlayer < 5f)
{
SetChaseBehavior();
}
else if (distanceToPlayer < 2f)
{
SetFleeBehavior();
}
else
{
SetPatrolBehavior();
}
// 执行当前行为
currentBehavior?.Execute();
}
public void SetPatrolBehavior()
{
if (!(currentBehavior is PatrolBehavior))
{
currentBehavior = new PatrolBehavior(transform, speed, patrolWaypoints);
Debug.Log("切换到巡逻行为");
}
}
public void SetChaseBehavior()
{
if (!(currentBehavior is ChaseBehavior))
{
currentBehavior = new ChaseBehavior(transform, player, speed);
Debug.Log("切换到追击行为");
}
}
public void SetFleeBehavior()
{
if (!(currentBehavior is FleeBehavior))
{
currentBehavior = new FleeBehavior(transform, player, speed);
Debug.Log("切换到逃跑行为");
}
}
}
示例2:输入控制策略
using UnityEngine;
// 输入策略接口
public interface IInputStrategy
{
Vector2 GetMovement();
bool GetJump();
bool GetFire();
}
// 键盘输入策略
public class KeyboardInputStrategy : IInputStrategy
{
public Vector2 GetMovement()
{
return new Vector2(
Input.GetAxis("Horizontal"),
Input.GetAxis("Vertical")
);
}
public bool GetJump()
{
return Input.GetKeyDown(KeyCode.Space);
}
public bool GetFire()
{
return Input.GetMouseButtonDown(0);
}
}
// 手柄输入策略
public class GamepadInputStrategy : IInputStrategy
{
public Vector2 GetMovement()
{
return new Vector2(
Input.GetAxis("GamepadHorizontal"),
Input.GetAxis("GamepadVertical")
);
}
public bool GetJump()
{
return Input.GetKeyDown(KeyCode.JoystickButton0); // A按钮
}
public bool GetFire()
{
return Input.GetKeyDown(KeyCode.JoystickButton1); // B按钮
}
}
// 触摸屏输入策略
public class TouchInputStrategy : IInputStrategy
{
private Vector2 touchStartPos;
private bool touchStarted = false;
public Vector2 GetMovement()
{
if (Input.touchCount > 0)
{
Touch touch = Input.GetTouch(0);
if (touch.phase == TouchPhase.Began)
{
touchStartPos = touch.position;
touchStarted = true;
}
else if (touch.phase == TouchPhase.Moved && touchStarted)
{
Vector2 delta = touch.position - touchStartPos;
return delta.normalized;
}
}
else
{
touchStarted = false;
}
return Vector2.zero;
}
public bool GetJump()
{
// 双指触摸视为跳跃
return Input.touchCount >= 2;
}
public bool GetFire()
{
// 三指触摸视为开火
return Input.touchCount >= 3;
}
}
// 输入管理器
public class InputManager : MonoBehaviour
{
private IInputStrategy currentInputStrategy;
private void Start()
{
// 根据平台自动选择输入策略
#if UNITY_STANDALONE || UNITY_EDITOR
currentInputStrategy = new KeyboardInputStrategy();
#elif UNITY_ANDROID || UNITY_IOS
currentInputStrategy = new TouchInputStrategy();
#else
currentInputStrategy = new GamepadInputStrategy();
#endif
}
private void Update()
{
// 可以添加动态切换输入策略的逻辑
if (Input.GetKeyDown(KeyCode.F1))
{
currentInputStrategy = new KeyboardInputStrategy();
Debug.Log("切换到键盘输入");
}
else if (Input.GetKeyDown(KeyCode.F2))
{
currentInputStrategy = new GamepadInputStrategy();
Debug.Log("切换到手柄输入");
}
}
public Vector2 GetMovementInput()
{
return currentInputStrategy.GetMovement();
}
public bool GetJumpInput()
{
return currentInputStrategy.GetJump();
}
public bool GetFireInput()
{
return currentInputStrategy.GetFire();
}
}
// 使用输入管理器的玩家控制器
public class PlayerController : MonoBehaviour
{
public float moveSpeed = 5f;
public float jumpForce = 10f;
private Rigidbody rb;
private InputManager inputManager;
private void Start()
{
rb = GetComponent<Rigidbody>();
inputManager = FindObjectOfType<InputManager>();
}
private void Update()
{
// 获取输入
Vector2 movement = inputManager.GetMovementInput();
bool jump = inputManager.GetJumpInput();
// 移动
Vector3 moveDirection = new Vector3(movement.x, 0, movement.y);
transform.Translate(moveDirection * moveSpeed * Time.deltaTime);
// 跳跃
if (jump && Mathf.Abs(rb.velocity.y) < 0.1f)
{
rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
}
}
}
示例3:游戏难度策略
using UnityEngine;
// 难度策略接口
public interface IDifficultyStrategy
{
float GetEnemySpeed();
float GetEnemySpawnRate();
int GetEnemyHealth();
string GetDifficultyName();
}
// 简单难度
public class EasyDifficulty : IDifficultyStrategy
{
public float GetEnemySpeed()
{
return 2f;
}
public float GetEnemySpawnRate()
{
return 3f;
}
public int GetEnemyHealth()
{
return 50;
}
public string GetDifficultyName()
{
return "简单";
}
}
// 普通难度
public class NormalDifficulty : IDifficultyStrategy
{
public float GetEnemySpeed()
{
return 3.5f;
}
public float GetEnemySpawnRate()
{
return 2f;
}
public int GetEnemyHealth()
{
return 100;
}
public string GetDifficultyName()
{
return "普通";
}
}
// 困难难度
public class HardDifficulty : IDifficultyStrategy
{
public float GetEnemySpeed()
{
return 5f;
}
public float GetEnemySpawnRate()
{
return 1f;
}
public int GetEnemyHealth()
{
return 200;
}
public string GetDifficultyName()
{
return "困难";
}
}
// 游戏管理器
public class GameManager : MonoBehaviour
{
private IDifficultyStrategy currentDifficulty;
private float nextSpawnTime;
public GameObject enemyPrefab;
public Transform[] spawnPoints;
private void Start()
{
// 默认设置为普通难度
SetDifficulty(new NormalDifficulty());
}
private void Update()
{
// 敌人生成逻辑
if (Time.time >= nextSpawnTime)
{
SpawnEnemy();
nextSpawnTime = Time.time + currentDifficulty.GetEnemySpawnRate();
}
}
private void SpawnEnemy()
{
Transform spawnPoint = spawnPoints[Random.Range(0, spawnPoints.Length)];
GameObject enemy = Instantiate(enemyPrefab, spawnPoint.position, spawnPoint.rotation);
// 根据难度设置敌人属性
EnemyController enemyController = enemy.GetComponent<EnemyController>();
enemyController.speed = currentDifficulty.GetEnemySpeed();
enemyController.health = currentDifficulty.GetEnemyHealth();
}
// 设置难度策略
public void SetDifficulty(IDifficultyStrategy difficulty)
{
currentDifficulty = difficulty;
Debug.Log("当前难度设置为: " + currentDifficulty.GetDifficultyName());
}
// UI按钮调用的方法
public void SetEasyDifficulty()
{
SetDifficulty(new EasyDifficulty());
}
public void SetNormalDifficulty()
{
SetDifficulty(new NormalDifficulty());
}
public void SetHardDifficulty()
{
SetDifficulty(new HardDifficulty());
}
}
// 敌人控制器
public class EnemyController : MonoBehaviour
{
public float speed;
public int health;
private Transform player;
private void Start()
{
player = GameObject.FindGameObjectWithTag("Player").transform;
}
private void Update()
{
// 简单的追击玩家逻辑
if (player != null)
{
Vector3 direction = (player.position - transform.position).normalized;
transform.position += direction * speed * Time.deltaTime;
}
}
public void TakeDamage(int damage)
{
health -= damage;
if (health <= 0)
{
Destroy(gameObject);
}
}
}
在Unity中的实现建议
- 使用ScriptableObject:可以将策略实现为ScriptableObject,便于在编辑器中配置
- 结合状态模式:策略模式常与状态模式结合使用,管理更复杂的行为
- 依赖注入:可以通过依赖注入框架来管理策略的创建和切换
- 编辑器扩展:为策略创建自定义编辑器,提高易用性
更多推荐


所有评论(0)