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 definir uma família de algoritmos, encapsular cada um deles e torná-los intercambiáveis em tempo de execução. Diferentemente do Observer, em que vários objetos reagem a um evento, o Strategy permite que um único objeto alterne dinamicamente entre diferentes comportamentos.

Em Go, implementamos 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 de estratégia, permitindo que o algoritmo seja substituído 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

Strategy é ideal quando você tem várias maneiras de realizar uma operação e precisa selecionar uma com base na entrada do usuário, na configuração ou nas condições 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 criar 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 concretas de formatação.

    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 e transforma o texto em letras minúsculas
    • TitleFormatter: coloca em maiúscula a primeira letra de cada palavra (as palavras são separadas por espaços)
  • processor.go: Crie sua struct de contexto que usa a estratégia de formatação.

    Crie uma struct TextProcessor que armazena 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

    Crie também um construtor NewTextProcessor() que retorne 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 quantidade de operações de formatação. Para cada operação, leia o tipo de formatação (upper, snake ou title), defina o formatador apropriado no processador, processe o texto original e imprima o resultado.

As entradas a seguir serão fornecidas:

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

Por exemplo, dada a entrada:

hello world today
3
upper
snake
title

Sua saída deverá ser:

HELLO WORLD TODAY
hello_world_today
Hello World Today

E dada a entrada:

Go Programming Language
2
snake
upper

Sua saída deverá ser:

go_programming_language
GO PROGRAMMING LANGUAGE

E dada a entrada:

design patterns are useful
1
title

Sua saída deverá ser:

Design Patterns Are Useful

Observe como o mesmo TextProcessor produz saídas completamente diferentes apenas trocando sua estratégia de formatação. 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!

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"),
		// defina o formatador apropriado no processor
		// e imprima 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

Pratique por conta própria: Compilador de Go online