Menu
Coddy logo textTech

Паттерн Strategy

Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy. Урок 90 из 107.

Паттерн Strategy позволяет определить семейство алгоритмов, инкапсулировать каждый из них и сделать их взаимозаменяемыми во время выполнения. В отличие от Observer, где несколько объектов реагируют на одно событие, Strategy позволяет одному объекту динамически переключаться между различными вариантами поведения.

В Go мы реализуем Strategy, определяя interface для алгоритма и внедряя различные реализации в структуру context:

type PaymentStrategy interface {
    Pay(amount float64) string
}

type CreditCard struct{}
func (c CreditCard) Pay(amount float64) string {
    return fmt.Sprintf("Paid %.2f via Credit Card", amount)
}

type PayPal struct{}
func (p PayPal) Pay(amount float64) string {
    return fmt.Sprintf("Paid %.2f via PayPal", amount)
}

Структура context хранит ссылку на интерфейс стратегии, что позволяет заменить алгоритм без изменения кода context:

type Checkout struct {
    strategy PaymentStrategy
}

func (c *Checkout) SetStrategy(s PaymentStrategy) {
    c.strategy = s
}

func (c *Checkout) ProcessPayment(amount float64) string {
    return c.strategy.Pay(amount)
}

Теперь вы можете изменить способ оплаты во время выполнения:

checkout := &Checkout{}

checkout.SetStrategy(CreditCard{})
fmt.Println(checkout.ProcessPayment(100.00))  // Оплачено 100.00 через Credit Card

checkout.SetStrategy(PayPal{})
fmt.Println(checkout.ProcessPayment(50.00))   // Оплачено 50.00 через PayPal

Паттерн Strategy идеально подходит, когда у вас есть несколько способов выполнить операцию и нужно выбрать один из них на основе пользовательского ввода, конфигурации или условий во время выполнения. Распространённые варианты использования включают алгоритмы сортировки, методы сжатия и правила валидации.

challenge icon

Задание

Легко

Давайте создадим систему форматирования текста с использованием паттерна Strategy! Вы создадите различные стратегии форматирования, которые можно переключать во время выполнения, позволяя одному и тому же обработчику текста создавать результаты в разных стилях без изменения его основного кода.

Вы организуете код в трёх файлах:

  • formatter.go: Define ваш интерфейс стратегии и конкретные стратегии форматирования.

    Create интерфейс TextFormatter с методом Format(text string) string, который преобразует текст согласно правилам стратегии.

    Implement три стратегии форматирования:

    • UppercaseFormatter: converts текст в верхний регистр
    • SnakeCaseFormatter: converts пробелы в символы подчёркивания и makes текст строчными буквами
    • TitleFormatter: capitalizes первую букву каждого слова (слова разделены пробелами)
  • processor.go: Create вашу структуру context, которая использует стратегию форматирования.

    Создайте структуру TextProcessor, которая holds стратегию TextFormatter. Добавьте следующие методы:

    • SetFormatter(f TextFormatter) для изменения active стратегии форматирования
    • Process(text string) string, которая applies current formatter к тексту

    Также создайте конструктор NewTextProcessor(), который возвращает processor без установленного initial formatter.

  • main.go: Demonstrate переключение стратегий во время выполнения.

    Прочитайте фрагмент текста, затем число operations форматирования. Для каждой operation прочитайте тип форматирования (upper, snake или title), установите appropriate formatter для вашего processor, обработайте исходный текст и выведите результат.

Будут предоставлены следующие входные данные:

  • Строка 1: Текст для форматирования
  • Строка 2: Number операций форматирования
  • Следующие строки: Тип форматирования для каждой operation

Например, для:

hello world today
3
upper
snake
title

Ваш результат должен быть таким:

HELLO WORLD TODAY
hello_world_today
Hello World Today

А для:

Go Programming Language
2
snake
upper

Ваш результат должен быть таким:

go_programming_language
GO PROGRAMMING LANGUAGE

А для:

design patterns are useful
1
title

Ваш результат должен быть таким:

Design Patterns Are Useful

Обратите внимание, как один и тот же TextProcessor создаёт полностью разные результаты только благодаря переключению своей стратегии formatter. Processor не знает и не заботится о том, какой именно formatter он использует. Он просто delegates выполнение текущей установленной стратегии!

Попробуйте сами

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)
	
	// Прочитать текст для форматирования
	text, _ := reader.ReadString('\n')
	text = strings.TrimSpace(text)
	
	// Прочитать количество операций
	countStr, _ := reader.ReadString('\n')
	count, _ := strconv.Atoi(strings.TrimSpace(countStr))
	
	// Создать новый текстовый процессор
	processor := NewTextProcessor()
	
	// Обработать каждую операцию форматирования
	for i := 0; i < count; i++ {
		formatType, _ := reader.ReadString('\n')
		formatType = strings.TrimSpace(formatType)
		
		// TODO: На основе formatType ("upper", "snake", или "title"),
		// установить соответствующий форматтер на процессоре
		// и вывести обработанный текст
		
	}
}
quiz iconПроверьте себя

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

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

Потренируйтесь самостоятельно: Онлайн-компилятор Go