C# 策略模式详解

一、策略模式基本概念

策略模式(Strategy Pattern)是一种行为设计模式,它定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户端。

核心思想

将算法或行为抽象为独立的策略类,使得它们可以独立于客户端而变化。

主要角色

  1. Context(上下文) :维护一个策略对象的引用,通过策略接口与具体策略交互
  2. Strategy(策略接口) :定义所有支持的算法或行为的公共接口
  3. ConcreteStrategy(具体策略) :实现策略接口的具体算法类

二、策略模式特点

优点

  1. 开闭原则:无需修改上下文就能引入新策略
  2. 消除条件语句:避免使用大量条件语句来选择算法
  3. 算法复用:相同策略可被不同上下文使用
  4. 算法独立:将算法实现与使用它的代码分离

缺点

  1. 客户端必须了解策略差异:客户端需要知道不同策略的区别以选择合适的策略
  2. 类数量增加:如果策略很多,会导致类数量膨胀
  3. 通信开销:策略与上下文之间需要通过接口通信,可能增加开销

三、策略模式实现

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();

四、策略模式使用场景

  1. 多种算法变体:当系统需要在多种算法中选择一种时
  2. 消除条件语句:当有多个条件语句决定使用哪种算法变体时
  3. 算法需要独立变化:当算法需要独立于使用它的客户端变化时
  4. 运行时选择算法:当需要在运行时选择不同算法时

典型应用场景

  1. 支付系统:支持多种支付方式(信用卡、支付宝、微信等)
  2. 排序算法:根据需要选择不同排序算法
  3. 压缩工具:支持不同压缩算法(ZIP、RAR、7z等)
  4. 导航系统:提供不同路线策略(最快、最短、最少收费等)
  5. 游戏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;
    }
}

六、策略模式最佳实践

  1. 合理设计策略接口:确保接口足够通用,能覆盖所有策略变体
  2. 策略无状态化:尽可能设计无状态的策略类,便于共享和复用
  3. 客户端了解策略:确保客户端代码了解不同策略的区别
  4. 避免策略膨胀:当策略过多时,考虑使用其他模式(如责任链)
  5. 组合使用模式:策略模式常与工厂模式、模板方法模式等结合使用

七、策略模式与其他模式的关系

  1. 与状态模式:

    • 策略模式:客户端主动选择策略,策略间通常不相互知晓
    • 状态模式:状态自动转换,状态间通常相互知晓
  2. 与命令模式:

    • 策略模式:关注算法/策略的选择
    • 命令模式:关注请求的封装和参数化
  3. 与模板方法模式:

    • 策略模式:通过对象组合实现算法变化
    • 模板方法模式:通过类继承实现算法部分变化

八、总结

策略模式是处理算法变化的强大工具,它通过以下方式提高代码质量:

  • 消除复杂的条件判断
  • 将算法实现与使用它的代码分离
  • 使系统更容易扩展新算法
  • 符合开闭原则

在C#中实现策略模式时,关键点是:

  1. 明确定义策略接口
  2. 将可变行为封装到具体策略类中
  3. 通过组合而非继承来使用策略
  4. 根据需求选择简单或复杂的实现方式

正确使用策略模式可以使系统更加灵活和可维护,特别适合那些有多种算法变体且需要动态切换的场景

九、在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中的实现建议

  1. 使用ScriptableObject:可以将策略实现为ScriptableObject,便于在编辑器中配置
  2. 结合状态模式:策略模式常与状态模式结合使用,管理更复杂的行为
  3. 依赖注入:可以通过依赖注入框架来管理策略的创建和切换
  4. 编辑器扩展:为策略创建自定义编辑器,提高易用性

更多推荐