Menu
Coddy logo textTech

Паттерн 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 — при этом не требуется вносить изменения в существующий код.

challenge icon

Задание

Легко

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

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

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