Паттерн Strategy
Часть раздела Объектно-ориентированное программирование путешествия по Dart на Coddy — урок 93 из 110.
Паттерн Стратегия позволяет определить семейство алгоритмов, инкапсулировать каждый из них в отдельный класс и сделать их взаимозаменяемыми во время выполнения. Вместо жесткого кодирования поведения в классе, вы внедряете поведение через интерфейс, что позволяет менять стратегии без изменения класса, который их использует.
Рассмотрим платежную систему, в которой вы хотите поддерживать различные способы оплаты. Вместо использования условных операторов для обработки каждого метода, вы определяете интерфейс стратегии и создаете конкретные реализации:
abstract class PaymentStrategy {
void pay(double amount);
}
class CreditCardPayment implements PaymentStrategy {
@override
void pay(double amount) => print('Paid \$amount with Credit Card');
}
class PayPalPayment implements PaymentStrategy {
@override
void pay(double amount) => print('Paid \$amount with PayPal');
}
class ShoppingCart {
PaymentStrategy _paymentStrategy;
ShoppingCart(this._paymentStrategy);
void setPaymentStrategy(PaymentStrategy strategy) {
_paymentStrategy = strategy;
}
void checkout(double total) {
_paymentStrategy.pay(total);
}
}
void main() {
var cart = ShoppingCart(CreditCardPayment());
cart.checkout(99.99); // Оплачено $99.99 с помощью кредитной карты
cart.setPaymentStrategy(PayPalPayment());
cart.checkout(49.99); // Оплачено $49.99 через PayPal
}ShoppingCart не знает и не заботится о том, как работает оплата — он просто делегирует выполнение той стратегии, которая установлена в данный момент. Вы можете добавлять новые способы оплаты, создавая новые классы стратегий, не затрагивая код корзины. Этот паттерн идеален, когда у вас есть несколько способов выполнить действие и вы хотите выбирать или переключаться между ними динамически.
Задание
ЛегкоДавайте создадим систему форматирования текста, используя паттерн Стратегия (Strategy)! Вы создадите систему, в которой к тексту можно применять различные стратегии форматирования, что позволит пользователям динамически переключаться между форматами без изменения основного процессора текста.
Вы организуете свой код в двух файлах:
formatting_strategies.dart: Создайте здесь свою систему стратегий. Определите абстрактный классFormattingStrategyс методомString format(String text). Затем реализуйте три конкретные стратегии:UppercaseStrategy— преобразует текст в верхний регистрLowercaseStrategy— преобразует текст в нижний регистрTitleCaseStrategy— делает заглавной первую букву каждого слова (разделяйте по пробелам, делайте первую букву каждого слова заглавной, остальные — строчными)
TextProcessor, который содержитFormattingStrategy. У него должен быть конструктор, принимающий начальную стратегию, методsetStrategy()для смены стратегии во время выполнения и методprocess(String text), который применяет текущую стратегию и возвращает отформатированный результат.main.dart: Импортируйте ваши стратегии форматирования и продемонстрируйте, как паттерн Стратегия позволяет динамически переключать поведение. СоздайтеTextProcessor, начиная сUppercaseStrategy. Обработайте текстhello worldи выведите результат. Затем переключитесь наLowercaseStrategy, обработайтеHELLO WORLDи выведите результат. Наконец, переключитесь наTitleCaseStrategy, обработайтеthe quick brown foxи выведите результат.
Это демонстрирует мощь паттерна Стратегия — вашему TextProcessor не нужно знать подробности того, как работает форматирование. Он просто делегирует задачу той стратегии, которая установлена в данный момент, что позволяет легко добавлять новые варианты форматирования без изменения самого процессора!
Ожидаемый вывод:
HELLO WORLD
hello world
The Quick Brown FoxШпаргалка
Паттерн «Стратегия» (Strategy pattern) определяет семейство алгоритмов, инкапсулирует каждый из них в отдельный класс и делает их взаимозаменяемыми во время выполнения. Вместо жесткого кодирования поведения вы внедряете его через интерфейс.
Определите интерфейс стратегии:
abstract class PaymentStrategy {
void pay(double amount);
}Создайте конкретные реализации стратегии:
class CreditCardPayment implements PaymentStrategy {
@override
void pay(double amount) => print('Paid \$amount with Credit Card');
}
class PayPalPayment implements PaymentStrategy {
@override
void pay(double amount) => print('Paid \$amount with PayPal');
}Создайте класс контекста, который использует стратегию:
class ShoppingCart {
PaymentStrategy _paymentStrategy;
ShoppingCart(this._paymentStrategy);
void setPaymentStrategy(PaymentStrategy strategy) {
_paymentStrategy = strategy;
}
void checkout(double total) {
_paymentStrategy.pay(total);
}
}Используйте паттерн, внедряя и переключая стратегии:
var cart = ShoppingCart(CreditCardPayment());
cart.checkout(99.99); // Оплачено $99.99 с помощью Credit Card
cart.setPaymentStrategy(PayPalPayment());
cart.checkout(49.99); // Оплачено $49.99 с помощью PayPalКласс контекста делегирует поведение стратегии, не зная деталей реализации. Добавляйте новые стратегии, создавая новые классы без изменения существующего кода.
Попробуйте сами
import 'formatting_strategies.dart';
void main() {
// TODO: Создайте TextProcessor, начиная с UppercaseStrategy
// TODO: Обработайте текст 'hello world' и выведите результат
// TODO: Переключитесь на LowercaseStrategy
// TODO: Обработайте 'HELLO WORLD' и выведите результат
// TODO: Переключитесь на TitleCaseStrategy
// TODO: Обработайте 'the quick brown fox' и выведите результат
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыБиблиотеки и импортВведение в ООПКлассы и объектыКлючевое слово thisМетодыПеременные экземпляраОсновы конструкторовИтоги — Простой калькулятор4Null-безопасность
Введение в Null-безопасностьNullable и Non-Nullable типыОператоры ? и !Ключевое слово Late и Null-безопасностьNull-aware операторыNull-безопасность в классахИтоги — Система профилей пользователей7Абстрактные классы и интерфейсы
Абстрактные классыАбстрактные методыИнтерфейсы в DartНеявные интерфейсыРеализация против наследованияМножественные интерфейсыИтоги — Калькулятор фигур10Коллекции и обобщения
Обзор List, Set, MapТипобезопасные коллекцииОбобщенные классыОбобщенные методыОграничения обобщенийIterable и IteratorИтоги: Обобщенное хранилище13Продвинутые концепции ООП
Композиция против наследованияМетоды расширенияВызываемые классыЗапечатанные классы (Dart 3)Записи (Dart 3)Паттерны и сопоставление (3.0)Перечисления с методами16Проект: Управление библиотекой
Обзор проектаКлассы Book и User2Конструкторы в Dart
Конструктор по умолчаниюИменованные конструкторыСписки инициализацииКонстантные конструкторыФабричные конструкторыПеренаправляющие конструкторыИтоги — Shape Builder5Инкапсуляция
Публичные и приватные членыСоглашение о префиксе _Приватность на уровне библиотекГеттеры и сеттеры: подробный разборСкрытие данныхИтоги — Записи о студентах8Mixins
Введение в MixinsСоздание MixinsИспользование нескольких MixinsКлючевое слово on в MixinsMixin и наследованиеMixin и интерфейсИтоги: Система животных11Специальные методы
Переопределение toString()Переопределение hashCode и ==Интерфейс ComparableМетод call()Переопределение noSuchMethodПовторение — Пользовательская коллекция14Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн ObserverПаттерн Strategy3Свойства классов
Члены экземпляра и статические членыПоля Final и ConstПеременные LateСтатические методы и поляГеттеры и сеттерыИтоги — Менеджер банковского счета6Наследование
Основы наследованияКлючевое слово superПереопределение методовАннотация @overrideКлючевое слово final для классовКонструкторы и наследованиеИтоги: Иерархия сотрудников9Полиморфизм
Основы полиморфизмаПолиморфизм через интерфейсыПроверка типов во время выполненияОператоры is и asКлючевое слово covariantИтоги — Обработчик платежей12Асинхронное ООП
Futures и async/awaitОсновы StreamsКонтроллеры StreamАсинхронные конструкторыАсинхронность в методах классовИтоги — Data Fetcher15Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикПаттерн Репозиторий