Menu
Coddy logo textTech

Паттерн 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 не знает и не заботится о том, как работает оплата — он просто делегирует выполнение той стратегии, которая установлена в данный момент. Вы можете добавлять новые способы оплаты, создавая новые классы стратегий, не затрагивая код корзины. Этот паттерн идеален, когда у вас есть несколько способов выполнить действие и вы хотите выбирать или переключаться между ними динамически.

challenge icon

Задание

Легко

Давайте создадим систему форматирования текста, используя паттерн Стратегия (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' и выведите результат
}
quiz iconПроверьте себя

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

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