Menu
Coddy logo textTech

Паттерн Strategy

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

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

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

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: Define интерфейс IShippingStrategy в пространстве имён Shipping с method CalculateCost(double weight), который возвращает значение типа double. Затем создайте три конкретных класса стратегий:
    • StandardShipping — вычисляет стоимость как weight * 1.5
    • ExpressShipping — вычисляет стоимость как weight * 3.0 + 5.0 (включает фиксированную плату за экспресс-доставку)
    • OvernightShipping — вычисляет стоимость как weight * 5.0 + 10.0 (премиум-услуга с более высокой базовой платой)
  • OrderProcessor.cs: Create class OrderProcessor в том же пространстве имён. Это ваш контекстный class, который использует стратегию доставки. Он должен содержать:
    • Приватный field для хранения текущего значения IShippingStrategy
    • method SetShippingMethod(IShippingStrategy strategy) для изменения стратегии доставки
    • method ProcessOrder(double weight), который использует текущую стратегию для вычисления и возврата стоимости доставки
  • Program.cs: Объедините всё вместе, создав OrderProcessor и продемонстрировав, как можно менять стратегии доставки во время выполнения. Один и тот же обработчик может вычислять стоимость с использованием разных алгоритмов, просто изменяя свою стратегию.

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

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

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

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

18.5

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

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

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Проверьте себя

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

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

Потренируйтесь самостоятельно: Онлайн-компилятор C#