Паттерн Strategy
Часть раздела Объектно-ориентированное программирование путешествия по Java на Coddy — урок 69 из 87.
Паттерн Стратегия (Strategy Pattern) — это поведенческий паттерн проектирования, который позволяет определить семейство алгоритмов, инкапсулировать каждый из них в отдельный класс и сделать их взаимозаменяемыми. Вместо жесткого кодирования конкретного поведения вы можете менять алгоритмы во время выполнения программы, не изменяя код, который их использует.
Представьте себе платежную систему, которая поддерживает несколько способов оплаты. Без паттерна «Стратегия» вы в конечном итоге получите запутанную условную логику. С ним каждый способ оплаты становится отдельной стратегией:
interface PaymentStrategy {
void pay(int amount);
}
class CreditCardPayment implements PaymentStrategy {
public void pay(int amount) {
System.out.println("Paid " + amount + " via Credit Card");
}
}
class PayPalPayment implements PaymentStrategy {
public void pay(int amount) {
System.out.println("Paid " + amount + " via PayPal");
}
}Класс context хранит ссылку на стратегию и делегирует ей работу. Ключевым моментом является то, что контекст не знает, какую конкретную стратегию он использует — он знает только интерфейс:
class ShoppingCart {
private PaymentStrategy paymentStrategy;
public void setPaymentStrategy(PaymentStrategy strategy) {
this.paymentStrategy = strategy;
}
public void checkout(int amount) {
paymentStrategy.pay(amount);
}
}Теперь вы можете динамически изменять поведение оплаты:
ShoppingCart cart = new ShoppingCart();
cart.setPaymentStrategy(new CreditCardPayment());
cart.checkout(100); // Оплачено 100 через Credit Card
cart.setPaymentStrategy(new PayPalPayment());
cart.checkout(50); // Оплачено 50 через PayPalПаттерн «Стратегия» (Strategy Pattern) проявляет себя наилучшим образом, когда у вас есть несколько способов выполнения действия и вы хотите легко переключаться между ними. Добавление нового способа оплаты просто означает создание нового класса, который реализует PaymentStrategy — при этом не требуется вносить изменения в существующий код.
Задание
ЛегкоДавайте создадим систему форматирования текста, используя паттерн Стратегия (Strategy Pattern)! Вы создадите систему, в которой различные стратегии форматирования можно менять во время выполнения для преобразования текста различными способами — это идеально подходит для приложения, которому необходимо поддерживать несколько форматов вывода.
Вы организуете свой код в четырех файлах:
TextFormatter.java: Определите интерфейсTextFormatter, который будут реализовывать все стратегии форматирования. Он должен объявлять один методString format(String text), который принимает строку и возвращает отформатированную версию.Formatters.java: Создайте три конкретные стратегии, реализующие ваш интерфейсTextFormatter:UpperCaseFormatter— преобразует весь текст в заглавные буквы.LowerCaseFormatter— преобразует весь текст в строчные буквы.TitleCaseFormatter— делает заглавной первую букву каждого слова, а остальные буквы — строчными. Слова разделяются пробелами.TextEditor.java: Создайте класс контекста, который использует стратегию форматирования. ВашTextEditorдолжен иметь:Приватное поле для хранения текущей стратегии
TextFormatter.Метод
setFormatter(TextFormatter formatter), который меняет активную стратегию форматирования.Метод
publishText(String text), который применяет текущий форматер к тексту и выводитPublished: [formatted text]. Если форматер не был установлен, он должен вывестиPublished: [original text]без изменений.Main.java: Объедините вашу систему стратегий! Вы получите два входных значения: текст для форматирования (String) и стиль форматирования (String:"upper","lower"или"title").Создайте
TextEditorи сначала вызовитеpublishTextс вашим входным текстом перед установкой какого-либо форматера (демонстрируя поведение по умолчанию).Затем, основываясь на входном стиле, установите соответствующую стратегию форматера и снова вызовите
publishTextс тем же текстом, чтобы показать отформатированный результат.
Вы получите два входных значения по порядку: текст для форматирования (String) и стиль форматирования (String).
Например, при входных данных hello world и title ваш вывод будет следующим:
Published: hello world
Published: Hello WorldОбратите внимание, что TextEditor ничего не знает о конкретной логике форматирования — он просто делегирует задачу той стратегии, которая установлена в данный момент. Это позволяет легко добавлять новые стили форматирования, не затрагивая код редактора!
Шпаргалка
Паттерн Стратегия (Strategy Pattern) — это поведенческий паттерн проектирования, который определяет семейство алгоритмов, инкапсулирует каждый из них в отдельный класс и делает их взаимозаменяемыми. Он позволяет изменять алгоритмы во время выполнения программы без изменения кода, который их использует.
Ключевые компоненты
Интерфейс стратегии (Strategy Interface): Определяет общий интерфейс для всех конкретных стратегий.
interface PaymentStrategy {
void pay(int amount);
}Конкретные стратегии (Concrete Strategies): Реализуют интерфейс стратегии с использованием специфических алгоритмов.
class CreditCardPayment implements PaymentStrategy {
public void pay(int amount) {
System.out.println("Paid " + amount + " via Credit Card");
}
}
class PayPalPayment implements PaymentStrategy {
public void pay(int amount) {
System.out.println("Paid " + amount + " via PayPal");
}
}Класс контекста (Context Class): Хранит ссылку на стратегию и делегирует ей работу. Контекст знает только об интерфейсе, а не о конкретной реализации.
class ShoppingCart {
private PaymentStrategy paymentStrategy;
public void setPaymentStrategy(PaymentStrategy strategy) {
this.paymentStrategy = strategy;
}
public void checkout(int amount) {
paymentStrategy.pay(amount);
}
}Использование
Стратегии можно менять динамически во время выполнения:
ShoppingCart cart = new ShoppingCart();
cart.setPaymentStrategy(new CreditCardPayment());
cart.checkout(100); // Paid 100 via Credit Card
cart.setPaymentStrategy(new PayPalPayment());
cart.checkout(50); // Paid 50 via PayPalПреимущества
Паттерн Стратегия идеален, когда у вас есть несколько способов выполнения действия и вы хотите легко переключаться между ними. Добавление новой стратегии просто означает создание нового класса, реализующего интерфейс стратегии — никаких изменений в существующем коде не требуется.
Попробуйте сами
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String text = scanner.nextLine();
String style = scanner.nextLine();
// TODO: Создайте экземпляр TextEditor
// TODO: Вызовите publishText с входным текстом (до установки какого-либо форматировщика)
// TODO: В зависимости от входного стиля ("upper", "lower" или "title"),
// установите соответствующий форматировщик и снова вызовите publishText
}
}В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыКлючевое слово thisМетодыПоля (атрибуты)Метод-конструкторПерегрузка конструкторовИтоги — Простой калькулятор4Наследование
Основы наследования (extends)Ключевое слово superПереопределение методов (@Override)Цепочка конструкторовКласс ObjectОдиночное и многоуровневое наследованиеПочему нет множественного наследования классовИтоги — Иерархия сотрудников7Специальные методы и класс Object
Метод toString()equals() и hashCode()Метод clone()compareTo() и ComparableИнтерфейс ComparatorПовторение — Кастомная сортировка2Модификаторы доступа и инкапсуляция
Обзор уровней доступаМетоды геттеры и сеттерыСокрытие данныхКлючевое слово finalИтоги — Менеджер банковского счета5Полиморфизм
Основы перегрузки методовПереопределение методов (Run-Time)Upcasting и DowncastingОператор instanceofАбстрактные классы и методыИтоги: Калькулятор фигур8Продвинутые концепции ООП
Композиция против наследованияАгрегация против композицииВнутренние, вложенные и анонимные классыEnums и методы перечисленийRecords (Java 16+)Sealed-классы (Java 17+)11Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн BuilderПаттерн ObserverПаттерн Strategy3Свойства класса и статические члены
Переменные экземпляра и статические переменныеСтатические методыСтатические блокиКонстанты (static final)Итоги: Счетчик и утилиты6Интерфейсы и абстрактные классы
Введение в интерфейсыРеализация интерфейсовРеализация нескольких интерфейсовDefault и Static методы в интерфейсахАбстрактные классы vs ИнтерфейсыФункциональные интерфейсыИтоги — Платежная система9Обобщения
Введение в обобщенияОбобщенные классыОбобщенные методыОграниченные параметры типаСимволы подстановки (Wildcards: ?, extends, super)Итоги — Обобщенный контейнер12Паттерны проектирования. Часть 2
Паттерн «Команда»Паттерн «Адаптер»Паттерн «Декоратор»Паттерн «Шаблонный метод»Паттерн «Состояние»Паттерн «Компоновщик»Паттерн «Итератор»