Паттерн 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 не требуются. Это делает ваш код открытым для расширения, но закрытым для модификации.
Задание
ЛегкоДавайте создадим калькулятор стоимости доставки, используя паттерн Strategy. Разные методы доставки используют разные алгоритмы расчёта стоимости, и ваша система должна позволять переключаться между ними во время выполнения, не изменяя основную логику обработки заказа.
Вы организуете код в трёх файлах:
ShippingStrategy.cs: Define интерфейсIShippingStrategyв пространстве имёнShippingс methodCalculateCost(double weight), который возвращает значение типаdouble. Затем создайте три конкретных класса стратегий:StandardShipping— вычисляет стоимость какweight * 1.5ExpressShipping— вычисляет стоимость какweight * 3.0 + 5.0(включает фиксированную плату за экспресс-доставку)OvernightShipping— вычисляет стоимость какweight * 5.0 + 10.0(премиум-услуга с более высокой базовой платой)
OrderProcessor.cs: Create classOrderProcessorв том же пространстве имён. Это ваш контекстный class, который использует стратегию доставки. Он должен содержать:- Приватный field для хранения текущего значения
IShippingStrategy - method
SetShippingMethod(IShippingStrategy strategy)для изменения стратегии доставки - method
ProcessOrder(double weight), который использует текущую стратегию для вычисления и возврата стоимости доставки
- Приватный field для хранения текущего значения
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: Обработать заказ и вывести рассчитанную стоимость
}
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыПространства имен и директивыВведение в классы и объектыКлючевое слово 'this'Методы и параметрыПоля и свойстваКонструкторыИнициализаторы объектовИтоги — Простой калькулятор4Наследование
Основы синтаксиса наследования (:)Ключевое слово 'base'Ключевые слова Virtual и OverrideЗапечатанные классыБазовый класс 'object'Итоги — Иерархия сотрудников7Продвинутые возможности
Перегрузка операторовИндексаторы (this[])Переопределение ToString()Методы расширенияПовторение — Пользовательский список10Паттерны проектирования. Часть 1
Введение в паттерны проектированияПотокобезопасный SingletonПаттерн FactoryПаттерн Observer (События)Паттерн Strategy13Финальные испытания
Платформа для онлайн-обученияКомпонент игрового персонажаСервис аренды транспорта2Свойства и статические члены
Автоматически реализуемые свойстваСвойства только для чтения и записиСтатические поля и методыСтатические классыЧлены в виде выражений5Полиморфизм и интерфейсы
Полиморфизм: компиляция vs выполнениеИнтерфейс vs абстрактный классМножественные интерфейсыЯвные интерфейсыUpcasting и DowncastingИтоги: Калькулятор фигур8Продвинутые концепции ООП
Композиция вместо наследованияGenerics (классы и методы)Делегаты и событияАтрибуты и рефлексияIDisposable и оператор usingОсновы Dependency Injection11Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн Компоновщик3Архитектура классов
Экземплярные и статические данныеКлючевые слова 'readonly' и 'const'Опорные поляПовторение — Менеджер банковских счетов6Инкапсуляция
Модификаторы доступаСвойства для инкапсуляцииРеализация скрытия данныхПаттерны неизменяемостиИтоги — Записи студентов9Переменное количество аргументов
Ключевое слово 'params'Необязательные параметрыИменованные аргументыПерегрузка методовПотренируйтесь самостоятельно: Онлайн-компилятор C#