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 PayPalStrategy é 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.
Desafio
FácilVamos 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
TextFormattercom um métodoFormat(text string) stringque 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úsculasSnakeCaseFormatter: converte espaços em sublinhados e transforma o texto em letras minúsculasTitleFormatter: 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
TextProcessorque armazena uma estratégiaTextFormatter. Adicione estes métodos:SetFormatter(f TextFormatter)para alterar a estratégia de formatação ativaProcess(text string) stringque 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,snakeoutitle), 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
titleSua saída deverá ser:
HELLO WORLD TODAY
hello_world_today
Hello World TodayE dada a entrada:
Go Programming Language
2
snake
upperSua saída deverá ser:
go_programming_language
GO PROGRAMMING LANGUAGEE dada a entrada:
design patterns are useful
1
titleSua saída deverá ser:
Design Patterns Are UsefulObserve 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
}
}
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
1Fundamentos de OOP em Go
Arquivos ExternosWorkspace e Módulos do GoPacotes e ImportsNomes Exportados vs. Não ExportadosIntrodução à OOP em GoStructs como ClassesDefinindo Métodos em StructsReceptores de Ponteiro vs. de ValorInicialização de StructsFunções ConstrutorasRecapitulação — Calculadora Simples4Interfaces
Introdução às InterfacesImplementação ImplícitaInterface como ContratoInterface Vazia (any)Asserção de TipoSwitch de TipoComposição de InterfacesInterfaces Stringer e ErrorRecapitulação - Calculadora de Formas7Encapsulamento
Campos Exportados vs. Não ExportadosEncapsulamento no Nível de PacoteMétodos Getters e SettersOcultação de Informações em GoRecapitulação - Registros de Alunos10Genéricos (Go 1.18+)
Introdução aos genéricosParâmetros de tipoRestrições de tipoStructs genéricasAlternativa para métodos genéricosRecapitulação - Coleção genérica13Padrões de Projeto — Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão Abstract FactoryPadrão ObserverPadrão Strategy2Tipos e Structs em Profundidade
Tipos Básicos e CompostosDefinições de Tipos PersonalizadosTags de StructStructs AnônimasStructs AninhadasValores Zero e PadrõesRecapitulação - Livro de Contatos5Composição em vez de herança
Por que Go não tem herançaNoções básicas de incorporação de structsPromoção de métodosIncorporação de várias structsIncorporação vs agregaçãoOcultação de métodos incorporadosRecapitulação - Hierarquia de funcionários8Tratamento de Erros e OOP
A interface errorTipos de Erro PersonalizadosEncapsulamento de Erros (fmt.Errorf)Erros Sentinelaerrors.Is() e errors.As()Panic, Defer e RecoverRecapitulação - Analisador de Arquivos3Ponteiros e Memória
Fundamentos de Ponteiros em GoPonteiros para StructsPassagem por Valor vs. ReferênciaA Função new()Coleta de Lixo em GoRecapitulação - Construtor de Lista Ligada6Polimorfismo em Go
Polimorfismo via InterfacesDuck Typing em GoRegras de Satisfação de InterfacesColeções PolimórficasInjeção de DependênciasRecapitulação - Processador de Pagamentos9Concorrência e OOP
Fundamentos de GoroutinesCanais e comunicaçãoCanais com e sem bufferInstrução selectsync.Mutex e sync.RWMutexsync.WaitGroupDesign de structs thread-safeRevisão - Worker PoolPratique por conta própria: Compilador de Go online