Для чего нужен паттерн Strategy?csharp-63

Суть паттерна Strategy

Strategy (Стратегия) — это поведенческий паттерн проектирования, который определяет семейство алгоритмов, инкапсулирует каждый из них и делает их взаимозаменяемыми. Он позволяет изменять алгоритмы независимо от клиентов, которые ими пользуются.

Основные компоненты:

  1. Контекст (Context) - класс, который использует стратегию
  2. Интерфейс стратегии - определяет как контекст будет вызывать алгоритм
  3. Конкретные стратегии - реализации интерфейса для различных алгоритмов

Классическая реализация на C#

// Интерфейс стратегии
public interface ISortStrategy
{
    void Sort(List<int> dataset);
}

// Конкретные стратегии
public class BubbleSortStrategy : ISortStrategy
{
    public void Sort(List<int> dataset)
    {
        Console.WriteLine("Sorting using Bubble Sort");
        // Реализация алгоритма
    }
}

public class QuickSortStrategy : ISortStrategy
{
    public void Sort(List<int> dataset)
    {
        Console.WriteLine("Sorting using Quick Sort");
        // Реализация алгоритма
    }
}

// Контекст
public class Sorter
{
    private ISortStrategy _strategy;

    public Sorter(ISortStrategy strategy)
    {
        _strategy = strategy;
    }

    public void SetStrategy(ISortStrategy strategy)
    {
        _strategy = strategy;
    }

    public void ExecuteSort(List<int> dataset)
    {
        _strategy.Sort(dataset);
    }
}

// Использование
var dataset = new List<int> { 1, 5, 3, 2, 4 };
var sorter = new Sorter(new BubbleSortStrategy());
sorter.ExecuteSort(dataset);

sorter.SetStrategy(new QuickSortStrategy());
sorter.ExecuteSort(dataset);

Основные цели паттерна Strategy

  1. Инкапсуляция алгоритмов: Каждый алгоритм находится в собственном классе
  2. Возможность замены алгоритмов во время выполнения
  3. Избавление от условных операторов при выборе алгоритмов
  4. Соблюдение Open/Closed Principle: Новые стратегии можно добавлять без изменения существующего кода

Практические примеры применения

1. Платежные системы

public interface IPaymentStrategy
{
    void ProcessPayment(decimal amount);
}

public class CreditCardPayment : IPaymentStrategy { /*...*/ }
public class PayPalPayment : IPaymentStrategy { /*...*/ }
public class CryptoPayment : IPaymentStrategy { /*...*/ }

public class PaymentProcessor
{
    private IPaymentStrategy _strategy;

    public void SetPaymentStrategy(IPaymentStrategy strategy) => _strategy = strategy;
    public void ExecutePayment(decimal amount) => _strategy.ProcessPayment(amount);
}

2. Система скидок

public interface IDiscountStrategy
{
    decimal ApplyDiscount(decimal originalPrice);
}

public class RegularCustomerDiscount : IDiscountStrategy { /*...*/ }
public class VIPCustomerDiscount : IDiscountStrategy { /*...*/ }
public class HolidayDiscount : IDiscountStrategy { /*...*/ }

3. Обработка изображений

public interface IFilterStrategy
{
    Bitmap ApplyFilter(Bitmap image);
}

public class BlackWhiteFilter : IFilterStrategy { /*...*/ }
public class SepiaFilter : IFilterStrategy { /*...*/ }
public class BlurFilter : IFilterStrategy { /*...*/ }

Преимущества использования Strategy

  1. Гибкость: Легко добавлять новые алгоритмы
  2. Тестируемость: Каждую стратегию можно тестировать изолированно
  3. Чистый код: Избавляет от больших условных блоков
  4. Соблюдение SOLID:
    • Single Responsibility (каждая стратегия - одна ответственность)
    • Open/Closed (новые стратегии без модификации кода)
    • Dependency Inversion (зависимость от абстракций)

Сравнение с другими паттернами

  • В отличие от Factory, Strategy сосредоточен на поведении, а не на создании объектов
  • В отличие от State, стратегии не знают друг о друге и не переключаются автоматически
  • В отличие от Template Method, Strategy использует композицию вместо наследования

Оптимизации и улучшения

  1. Кэширование стратегий:
public class StrategyCache
{
    private readonly Dictionary<string, ISortStrategy> _strategies;

    public StrategyCache()
    {
        _strategies = new Dictionary<string, ISortStrategy>
        {
            ["bubble"] = new BubbleSortStrategy(),
            ["quick"] = new QuickSortStrategy()
        };
    }

    public ISortStrategy GetStrategy(string key) => _strategies[key];
}
  1. Лямбда-стратегии (использование делегатов):
public class Context
{
    private Action<List<int>> _sortStrategy;

    public void SetStrategy(Action<List<int>> strategy) => _sortStrategy = strategy;
    public void Execute(List<int> data) => _sortStrategy(data);
}

// Использование
var context = new Context();
context.SetStrategy(data => data.Sort()); // Используем встроенный Sort
context.Execute(items);

Резюмируем:

паттерн Strategy особенно полезен, когда в системе есть несколько вариантов одного алгоритма или когда необходимо легко расширять функциональность новыми способами выполнения операций. Он помогает создавать гибкие, легко поддерживаемые системы, соответствующие современным принципам разработки ПО.