Menu
Coddy logo textTech

Паттерн Strategy

Часть раздела Объектно-ориентированное программирование путешествия по C# на Coddy — урок 55 из 70.

Паттерн Стратегия — это поведенческий паттерн, который позволяет определить семейство алгоритмов, инкапсулировать каждый из них в отдельный класс и сделать их взаимозаменяемыми во время выполнения. Вместо жесткого кодирования поведения вы внедряете нужную стратегию в объект.

Паттерн состоит из трех частей: интерфейса стратегии, который определяет контракт алгоритма, конкретных классов стратегий, которые реализуют различные варианты поведения, и класса контекста, который использует стратегию:

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

public class CreditCardPayment : IPaymentStrategy
{
    public void Pay(decimal amount) => 
        Console.WriteLine($"Paid {amount} via Credit Card");
}

public class PayPalPayment : IPaymentStrategy
{
    public void Pay(decimal amount) => 
        Console.WriteLine($"Paid {amount} via PayPal");
}

public class ShoppingCart
{
    private IPaymentStrategy _paymentStrategy;
    
    public void SetPaymentMethod(IPaymentStrategy strategy)
    {
        _paymentStrategy = strategy;
    }
    
    public void Checkout(decimal total)
    {
        _paymentStrategy.Pay(total);
    }
}

Контекст (ShoppingCart) не знает, какой метод оплаты он использует — он просто вызывает Pay() для любой предоставленной стратегии:

var cart = new ShoppingCart();

cart.SetPaymentMethod(new CreditCardPayment());
cart.Checkout(99.99m);  // Оплачено 99.99 через Credit Card

cart.SetPaymentMethod(new PayPalPayment());
cart.Checkout(49.99m);  // Оплачено 49.99 через PayPal

Паттерн «Стратегия» (Strategy) устраняет сложную условную логику. Добавление нового способа оплаты означает создание нового класса, который реализует IPaymentStrategy — изменения в ShoppingCart не требуются. Это делает ваш код открытым для расширения, но закрытым для изменения.

challenge icon

Задание

Легко

Давайте создадим калькулятор стоимости доставки, используя паттерн Стратегия (Strategy). Различные методы доставки имеют разные алгоритмы ценообразования, и ваша система должна позволять переключаться между ними во время выполнения программы без изменения основной логики обработки заказов.

Вы организуете свой код в трех файлах:

  • ShippingStrategy.cs: Определите интерфейс IShippingStrategy в пространстве имен Shipping с методом CalculateCost(double weight), который возвращает double. Затем создайте три конкретных класса стратегий:
    • StandardShipping — рассчитывает стоимость как weight * 1.5
    • ExpressShipping — рассчитывает стоимость как weight * 3.0 + 5.0 (включает фиксированную плату за экспресс-доставку)
    • OvernightShipping — рассчитывает стоимость как weight * 5.0 + 10.0 (премиальная услуга с более высокой базовой платой)
  • OrderProcessor.cs: Создайте класс OrderProcessor в том же пространстве имен. Это ваш класс контекста, который использует стратегию доставки. Он должен иметь:
    • Приватное поле для хранения текущей IShippingStrategy
    • Метод SetShippingMethod(IShippingStrategy strategy) для смены стратегии доставки
    • Метод ProcessOrder(double weight), который использует текущую стратегию для расчета и возврата стоимости доставки
  • Program.cs: Объедините все вместе, создав OrderProcessor и продемонстрировав, как можно менять стратегии доставки во время выполнения. Один и тот же процессор может рассчитывать стоимость, используя разные алгоритмы, просто меняя свою стратегию.

Вы получите два входных значения:

  • Тип метода доставки: standard, express или overnight
  • Вес посылки в виде десятичного числа (например, 4.5)

Создайте OrderProcessor, установите соответствующую стратегию доставки на основе входного типа, затем обработайте заказ и выведите рассчитанную стоимость.

Например, если входные данные — express и 4.5, результат должен быть:

18.5

Это демонстрирует мощь паттерна Стратегия — ваш OrderProcessor сам по себе не содержит никакой логики расчета доставки. Он просто делегирует задачу той стратегии, которая ему была предоставлена, что позволяет легко добавлять новые методы доставки (например, DroneDelivery) без какой-либо модификации процессора!

Шпаргалка

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

Паттерн состоит из трех частей:

  • Интерфейс стратегии: определяет контракт алгоритма
  • Классы конкретных стратегий: реализуют различные варианты поведения
  • Класс контекста: использует стратегию, не зная деталей её реализации

Пример структуры:

// Интерфейс стратегии
public interface IPaymentStrategy
{
    void Pay(decimal amount);
}

// Конкретные стратегии
public class CreditCardPayment : IPaymentStrategy
{
    public void Pay(decimal amount) => 
        Console.WriteLine($"Paid {amount} via Credit Card");
}

public class PayPalPayment : IPaymentStrategy
{
    public void Pay(decimal amount) => 
        Console.WriteLine($"Paid {amount} via PayPal");
}

// Класс контекста
public class ShoppingCart
{
    private IPaymentStrategy _paymentStrategy;
    
    public void SetPaymentMethod(IPaymentStrategy strategy)
    {
        _paymentStrategy = strategy;
    }
    
    public void Checkout(decimal total)
    {
        _paymentStrategy.Pay(total);
    }
}

Использование — переключение стратегий во время выполнения:

var cart = new ShoppingCart();

cart.SetPaymentMethod(new CreditCardPayment());
cart.Checkout(99.99m);  // Вывод: Paid 99.99 via Credit Card

cart.SetPaymentMethod(new PayPalPayment());
cart.Checkout(49.99m);  // Вывод: Paid 49.99 via PayPal

Преимущества:

  • Устраняет сложную условную логику
  • Принцип открытости/закрытости: открыт для расширения, закрыт для изменения
  • Добавление новых стратегий требует только создания новых классов, без изменения существующего кода

Попробуйте сами

using System;
using Shipping;

class Program
{
    public static void Main(string[] args)
    {
        // Чтение входных данных
        string shippingType = Console.ReadLine();
        double weight = Convert.ToDouble(Console.ReadLine());

        // TODO: Создайте экземпляр OrderProcessor

        // TODO: На основе shippingType ("standard", "express" или "overnight"),
        // создайте соответствующую стратегию доставки и установите её в процессоре

        // TODO: Обработайте заказ и выведите рассчитанную стоимость
    }
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование