Menu
Coddy logo textTech

Padrão Strategy

Parte da seção Programação Orientada a Objetos do Journey de GO da Coddy — lição 90 de 107.

O padrão Strategy permite que você defina uma família de algoritmos, encapsule cada um deles e os torne intercambiáveis em tempo de execução. Ao contrário do Observer, onde múltiplos objetos reagem a um evento, o Strategy permite que um único objeto alterne entre diferentes comportamentos dinamicamente.

Em Go, implementamos o Strategy definindo uma interface para o algoritmo e injetando diferentes implementações em uma struct de contexto:

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)
}

A struct de contexto mantém uma referência para a interface strategy, permitindo que o algoritmo seja trocado sem alterar o código do contexto:

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)
}

Agora você pode alterar o método de pagamento em tempo de execução:

checkout := &Checkout{}

checkout.SetStrategy(CreditCard{})
fmt.Println(checkout.ProcessPayment(100.00))  // Pago 100.00 via Cartão de Crédito

checkout.SetStrategy(PayPal{})
fmt.Println(checkout.ProcessPayment(50.00))   // Pago 50.00 via PayPal

O padrão Strategy é ideal quando você tem várias maneiras de realizar uma operação e precisa selecionar uma com base na entrada do usuário, configuração ou condições de tempo de execução. Casos de uso comuns incluem algoritmos de ordenação, métodos de compressão e regras de validação.

challenge icon

Desafio

Fácil

Vamos construir um sistema de formatação de texto usando o padrão Strategy! Você criará diferentes estratégias de formatação que podem ser trocadas em tempo de execução, permitindo que o mesmo processador de texto produza diferentes estilos de saída sem alterar seu código principal.

Você organizará seu código em três arquivos:

  • formatter.go: Defina sua interface de estratégia e as estratégias de formatação concretas.

    Crie uma interface TextFormatter com um método Format(text string) string que transforma o texto de acordo com as regras da estratégia.

    Implemente três estratégias de formatação:

    • UppercaseFormatter — converte o texto para letras maiúsculas
    • SnakeCaseFormatter — converte espaços em sublinhados (underscores) e deixa o texto em letras minúsculas
    • TitleFormatter — capitaliza a primeira letra de cada palavra (palavras são separadas por espaços)
  • processor.go: Crie sua struct de contexto que utiliza a estratégia de formatação.

    Construa uma struct TextProcessor que contém uma estratégia TextFormatter. Adicione estes métodos:

    • SetFormatter(f TextFormatter) para alterar a estratégia de formatação ativa
    • Process(text string) string que aplica o formatador atual ao texto

    Também crie um construtor NewTextProcessor() que retorna um processador sem nenhum formatador inicial definido.

  • main.go: Demonstre a troca de estratégias em tempo de execução.

    Leia um trecho de texto e, em seguida, leia uma contagem de operações de formatação. Para cada operação, leia o tipo de formato (upper, snake, ou title), defina o formatador apropriado em seu processador, processe o texto original e imprima o resultado.

As seguintes entradas serão fornecidas:

  • Linha 1: O texto a ser formatado
  • Linha 2: Número de operações de formato
  • Linhas seguintes: Tipo de formato para cada operação

Por exemplo, dado:

hello world today
3
upper
snake
title

Sua saída deve ser:

HELLO WORLD TODAY
hello_world_today
Hello World Today

E dado:

Go Programming Language
2
snake
upper

Sua saída deve ser:

go_programming_language
GO PROGRAMMING LANGUAGE

E dado:

design patterns are useful
1
title

Sua saída deve ser:

Design Patterns Are Useful

Observe como o mesmo TextProcessor produz saídas completamente diferentes apenas trocando sua estratégia de formatador. O processador não sabe nem se importa com qual formatador específico está usando — ele simplesmente delega para qualquer estratégia que esteja definida no momento!

Folha de consulta

O padrão Strategy define uma família de algoritmos, encapsula cada um deles e os torna intercambiáveis em tempo de execução. Ele permite que um único objeto alterne entre diferentes comportamentos dinamicamente.

Defina uma interface para o algoritmo:

type PaymentStrategy interface {
    Pay(amount float64) string
}

Implemente estratégias concretas:

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)
}

Crie uma struct de contexto que mantém uma referência para a interface da estratégia:

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)
}

Altere as estratégias em tempo de execução:

checkout := &Checkout{}

checkout.SetStrategy(CreditCard{})
fmt.Println(checkout.ProcessPayment(100.00))  // Pago 100.00 via Credit Card

checkout.SetStrategy(PayPal{})
fmt.Println(checkout.ProcessPayment(50.00))   // Pago 50.00 via PayPal

O Strategy é ideal quando você tem várias maneiras de realizar uma operação e precisa selecionar uma com base na entrada do usuário, configuração ou condições de tempo de execução.

Experimente você mesmo

package main

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

func main() {
	reader := bufio.NewReader(os.Stdin)
	
	// Ler o texto para formatar
	text, _ := reader.ReadString('\n')
	text = strings.TrimSpace(text)
	
	// Ler o número de operações
	countStr, _ := reader.ReadString('\n')
	count, _ := strconv.Atoi(strings.TrimSpace(countStr))
	
	// Criar um novo processador de texto
	processor := NewTextProcessor()
	
	// Processar cada operação de formatação
	for i := 0; i < count; i++ {
		formatType, _ := reader.ReadString('\n')
		formatType = strings.TrimSpace(formatType)
		
		// TODO: Com base no formatType ("upper", "snake" ou "title"),
		// definir o formatador apropriado no processador
		// e imprimir o texto processado
		
	}
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação Orientada a Objetos