Padrão de Método Template
Parte da seção Programação Orientada a Objetos do Journey de GO da Coddy. Lição 94 de 107.
O padrão Template Method define o esqueleto de um algoritmo em um tipo base, permitindo que os subtipos substituam etapas específicas sem alterar a estrutura geral. Enquanto o Decorator adiciona comportamento envolvendo objetos, o Template Method controla o fluxo do algoritmo e permite a personalização de etapas individuais.
Em Go, como não há herança, implementamos esse padrão usando incorporação de structs combinada com interfaces. A struct "template" define a estrutura do algoritmo e chama métodos que podem ser personalizados:
type DataProcessor interface {
ReadData() string
ProcessData(data string) string
SaveData(result string) string
}
type BaseProcessor struct {
Impl DataProcessor
}
func (b *BaseProcessor) Execute() string {
data := b.Impl.ReadData()
result := b.Impl.ProcessData(data)
return b.Impl.SaveData(result)
}As implementações concretas fornecem suas próprias versões de cada etapa, enquanto o fluxo do algoritmo permanece inalterado:
type CSVProcessor struct{}
func (c CSVProcessor) ReadData() string { return "csv-data" }
func (c CSVProcessor) ProcessData(d string) string { return "processed-" + d }
func (c CSVProcessor) SaveData(r string) string { return "Saved: " + r }
type JSONProcessor struct{}
func (j JSONProcessor) ReadData() string { return "json-data" }
func (j JSONProcessor) ProcessData(d string) string { return "parsed-" + d }
func (j JSONProcessor) SaveData(r string) string { return "Stored: " + r }O método template Execute orquestra as etapas em uma ordem fixa:
csvProc := &BaseProcessor{Impl: CSVProcessor{}}
fmt.Println(csvProc.Execute()) // Salvo: processed-csv-data
jsonProc := &BaseProcessor{Impl: JSONProcessor{}}
fmt.Println(jsonProc.Execute()) // Armazenado: parsed-json-dataO Template Method é ideal quando você tem algoritmos que compartilham a mesma estrutura, mas diferem em etapas específicas, como pipelines de importação/exportação de dados, geradores de relatórios ou frameworks de teste.
Desafio
FácilVamos criar um sistema de geração de relatórios usando o padrão Template Method! Você criará uma estrutura na qual diferentes tipos de relatório seguem o mesmo processo de geração, coletando dados, formatando-os e produzindo o resultado, mas cada tipo de relatório personaliza essas etapas de maneira diferente.
Você organizará seu código em três arquivos:
report.go: Defina sua interface e o processador base que coordena o algoritmo de geração do relatório.Crie uma interface
ReportGeneratorcom três métodos que representam as etapas da geração do relatório:GatherData() string: recupera os dados brutos do relatórioFormatData(data string) string: transforma os dados no formato do relatórioOutputReport(formatted string) string: produz a mensagem de saída final
Crie uma struct
ReportProcessorque contenha uma implementação deReportGenerator. Adicione um métodoGenerate() stringque execute as três etapas na ordem: coletar, formatar e, em seguida, produzir a saída, retornando o resultado final.generators.go: Implemente geradores de relatórios concretos que personalizem cada etapa.Crie dois tipos de relatório:
SalesReportcom um campoRegion(string)GatherData()retornasales-data-[region]FormatData(data)retornaSALES REPORT: [data]OutputReport(formatted)retornaPrinted: [formatted]
InventoryReportcom um campoWarehouse(string)GatherData()retornainventory-[warehouse]FormatData(data)retorna*** [data] ***OutputReport(formatted)retornaExported: [formatted]
main.go: Demonstre como a mesma estrutura de algoritmo produz resultados diferentes com base na implementação.Leia o tipo de relatório (
salesouinventory) e o valor de configuração (região para vendas, armazém para inventário). Crie o gerador apropriado, envolva-o em umReportProcessor, chameGenerate()e imprima o resultado.
As seguintes entradas serão fornecidas:
- Linha 1: Tipo de relatório (
salesouinventory) - Linha 2: Valor de configuração (nome da região ou nome do armazém)
Por exemplo, dado:
sales
NorthSua saída deve ser:
Printed: SALES REPORT: sales-data-NorthE, dado:
inventory
MainHubSua saída deve ser:
Exported: *** inventory-MainHub ***E, dado:
sales
WestSua saída deve ser:
Printed: SALES REPORT: sales-data-WestObserve como o ReportProcessor sempre chama as mesmas três etapas na mesma ordem, mas cada tipo de relatório fornece sua própria implementação dessas etapas. O esqueleto do algoritmo permanece fixo, enquanto os detalhes variam: esse é o padrão Template Method em ação!
Experimente você mesmo
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Ler o tipo de relatório
scanner.Scan()
reportType := scanner.Text()
// Ler o valor de configuração (região ou armazém)
scanner.Scan()
configValue := scanner.Text()
// TODO: Crie o gerador apropriado com base em reportType
// - Se reportType for "sales", crie um SalesReport com Region definido como configValue
// - Se reportType for "inventory", crie um InventoryReport com Warehouse definido como configValue
// TODO: Crie um ReportProcessor com o gerador
// TODO: Chame Generate() e imprima o resultado
_ = reportType
_ = configValue
fmt.Println("TODO: Generate and print the report")
}
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 Arquivos11Biblioteca Padrão e POO
io.Reader e io.Writersort.InterfaceInterface fmt.Stringerencoding/json com StructsInterface http.HandlerRevisão - Modelos de API REST14Padrões de Projeto — Parte 2
Padrão de ComandoPadrão AdapterPadrão DecoratorPadrão de Método TemplatePadrão de EstadoPadrão CompositeMiddleware como Decorator3Ponteiros 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