Menu
Coddy logo textTech

Padrão Template Method

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 subtipos sobrescrevam etapas específicas sem alterar a estrutura geral. Enquanto o Decorator adiciona comportamento ao envolver objetos, o Template Method controla o fluxo do algoritmo e permite a customização de etapas individuais.

Em Go, como não há herança, implementamos esse padrão usando composição de structs (struct embedding) 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)
}

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())  // Saved: processed-csv-data

jsonProc := &BaseProcessor{Impl: JSONProcessor{}}
fmt.Println(jsonProc.Execute()) // Stored: parsed-json-data

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.

challenge icon

Desafio

Fácil

Vamos construir um sistema de geração de relatórios usando o padrão Template Method! Você criará um framework onde diferentes tipos de relatórios seguem o mesmo processo de geração — coleta de dados, formatação e saída do resultado — mas cada tipo de relatório personaliza essas etapas de forma diferente.

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

  • report.go: Defina sua interface e o processador base que orquestra o algoritmo de geração de relatórios.

    Crie uma interface ReportGenerator com três métodos que representam as etapas da geração de relatórios:

    • GatherData() string — recupera os dados brutos para o relatório
    • FormatData(data string) string — transforma os dados no formato do relatório
    • OutputReport(formatted string) string — produz a mensagem de saída final

    Construa uma struct ReportProcessor que contém uma implementação de ReportGenerator. Adicione um método Generate() string que executa as três etapas em ordem: coletar, formatar e, em seguida, gerar a saída — retornando o resultado final.

  • generators.go: Implemente geradores de relatórios concretos que personalizam cada etapa.

    Crie dois tipos de relatório:

    • SalesReport com um campo Region (string)
      • GatherData() retorna "sales-data-" + a região
      • FormatData(data) retorna "SALES REPORT: " + os dados
      • OutputReport(formatted) retorna "Printed: " + o conteúdo formatado
    • InventoryReport com um campo Warehouse (string)
      • GatherData() retorna "inventory-" + o armazém
      • FormatData(data) retorna "*** " + os dados + " ***"
      • OutputReport(formatted) retorna "Exported: " + o conteúdo formatado
  • main.go: Demonstre como a mesma estrutura de algoritmo produz resultados diferentes com base na implementação.

    Leia o tipo de relatório ("sales" ou "inventory") e o valor de configuração (região para vendas, armazém para inventário). Crie o gerador apropriado, envolva-o em um ReportProcessor, chame Generate() e imprima o resultado.

As seguintes entradas serão fornecidas:

  • Linha 1: Tipo de relatório ("sales" ou "inventory")
  • Linha 2: Valor de configuração (nome da região ou nome do armazém)

Por exemplo, dado:

sales
North

Sua saída deve ser:

Printed: SALES REPORT: sales-data-North

E dado:

inventory
MainHub

Sua saída deve ser:

Exported: *** inventory-MainHub ***

E dado:

sales
West

Sua saída deve ser:

Printed: SALES REPORT: sales-data-West

Observe 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!

Folha de consulta

O padrão Template Method define o esqueleto de um algoritmo em um tipo base, permitindo que subtipos sobrescrevam etapas específicas sem alterar a estrutura geral.

Em Go, implemente este padrão usando incorporação de structs (struct embedding) 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)
}

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 template method orquestra as etapas em uma ordem fixa:

csvProc := &BaseProcessor{Impl: CSVProcessor{}}
fmt.Println(csvProc.Execute())  // Saved: processed-csv-data

jsonProc := &BaseProcessor{Impl: JSONProcessor{}}
fmt.Println(jsonProc.Execute()) // Stored: parsed-json-data

O Template Method é ideal quando algoritmos 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.

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: Criar o gerador apropriado com base no reportType
	// - Se reportType for "sales", criar um SalesReport com Region definido como configValue
	// - Se reportType for "inventory", criar um InventoryReport com Warehouse definido como configValue
	
	// TODO: Criar um ReportProcessor com o gerador
	
	// TODO: Chamar Generate() e imprimir o resultado
	_ = reportType
	_ = configValue
	fmt.Println("TODO: Generate and print the report")
}
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