Patrón Template Method
Parte de la sección Programación Orientada a Objetos del Journey de GO de Coddy. Lección 94 de 107.
El patrón Template Method define el esqueleto de un algoritmo en un tipo base, lo que permite que los subtipos redefinan pasos específicos sin cambiar la estructura general. Mientras que Decorator añade comportamiento envolviendo objetos, Template Method controla el flujo del algoritmo y permite personalizar pasos individuales.
En Go, como no existe la herencia, implementamos este patrón usando la inclusión de estructuras combinada con interfaces. La estructura de «plantilla» define la estructura del algoritmo y llama a métodos que se pueden personalizar:
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)
}Las implementaciones concretas proporcionan sus propias versiones de cada paso, mientras el flujo del algoritmo permanece sin cambios:
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 }El método de plantilla Execute orquesta los pasos en un orden fijo:
csvProc := &BaseProcessor{Impl: CSVProcessor{}}
fmt.Println(csvProc.Execute()) // Guardado: processed-csv-data
jsonProc := &BaseProcessor{Impl: JSONProcessor{}}
fmt.Println(jsonProc.Execute()) // Almacenado: parsed-json-dataTemplate Method es ideal cuando tienes algoritmos que comparten la misma estructura, pero difieren en pasos específicos, como canalizaciones de importación/exportación de datos, generadores de informes o marcos de pruebas.
Desafío
Fácil¡Construyamos un sistema de generación de informes usando el patrón Template Method! Crearás un marco en el que los distintos tipos de informes siguen el mismo proceso de generación, recopilando datos, dándoles formato y generando el resultado, pero cada tipo de informe personaliza estos pasos de manera diferente.
Organizarás tu código en tres archivos:
report.go: Define tu interfaz y el procesador base que coordina el algoritmo de generación del informe.Crea una interfaz
ReportGeneratorcon tres métodos que representen los pasos de la generación del informe:GatherData() string: recupera los datos sin procesar del informeFormatData(data string) string: transforma los datos al formato del informeOutputReport(formatted string) string: produce el mensaje de salida final
Construye una estructura
ReportProcessorque contenga una implementación deReportGenerator. Agrega un métodoGenerate() stringque ejecute los tres pasos en orden: recopilar, dar formato y después generar la salida; este método debe devolver el resultado final.generators.go: Implementa generadores de informes concretos que personalicen cada paso.Crea dos tipos de informes:
SalesReportcon un campoRegion(string)GatherData()devuelvesales-data-[region]FormatData(data)devuelveSALES REPORT: [data]OutputReport(formatted)devuelvePrinted: [formatted]
InventoryReportcon un campoWarehouse(string)GatherData()devuelveinventory-[warehouse]FormatData(data)devuelve*** [data] ***OutputReport(formatted)devuelveExported: [formatted]
main.go: Demuestra cómo la misma estructura algorítmica produce resultados diferentes según la implementación.Lee el tipo de informe (
salesoinventory) y el valor de configuración (la región para sales o el almacén para inventory). Crea el generador correspondiente, envuélvelo en unReportProcessor, llama aGenerate()e imprime el resultado.
Se proporcionarán las siguientes entradas:
- Línea 1: Tipo de informe (
salesoinventory) - Línea 2: Valor de configuración (nombre de la región o nombre del almacén)
Por ejemplo, dada la siguiente entrada:
sales
NorthTu salida debería ser:
Printed: SALES REPORT: sales-data-NorthY dada la siguiente entrada:
inventory
MainHubTu salida debería ser:
Exported: *** inventory-MainHub ***Y dada la siguiente entrada:
sales
WestTu salida debería ser:
Printed: SALES REPORT: sales-data-WestObserva cómo ReportProcessor siempre llama a los mismos tres pasos en el mismo orden, pero cada tipo de informe proporciona su propia implementación de esos pasos. El esqueleto del algoritmo permanece fijo mientras los detalles varían: ¡ese es el patrón Template Method en acción!
Pruébalo tú mismo
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Leer el tipo de reporte
scanner.Scan()
reportType := scanner.Text()
// Leer el valor de configuración (región o almacén)
scanner.Scan()
configValue := scanner.Text()
// TODO: Crear el generador apropiado según reportType
// - Si reportType es "sales", crear un SalesReport con Region establecido a configValue
// - Si reportType es "inventory", crear un InventoryReport con Warehouse establecido a configValue
// TODO: Crear un ReportProcessor con el generador
// TODO: Llamar a Generate() e imprimir el resultado
_ = reportType
_ = configValue
fmt.Println("TODO: Generate and print the report")
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de POO en Go
Archivos externosEspacio de trabajo y módulos de GoPaquetes e importacionesNombres exportados vs. no exportadosIntroducción a la POO en GoStructs como clasesDefinición de métodos en structsReceptores de puntero vs. de valorInicialización de structsFunciones constructorasResumen - Calculadora simple4Interfaces
Introducción a las interfacesImplementación implícitaLa interfaz como contratoInterfaz vacía (any)Type AssertionType SwitchComposición de interfacesInterfaces Stringer y ErrorResumen: Calculadora de figuras7Encapsulamiento
Campos exportados vs. no exportadosEncapsulamiento a nivel de paqueteMétodos Getter y SetterOcultamiento de información en GoResumen - Registros de estudiantes10Genéricos (Go 1.18+)
Introducción a los GenéricosParámetros de tipoRestricciones de tipoStructs genéricosSolución alternativa para métodos genéricosResumen - Colección genérica13Patrones de diseño - Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón Abstract FactoryPatrón ObserverPatrón Strategy2Inmersión profunda en Tipos y Structs
Tipos básicos y compuestosDefiniciones de tipos personalizadosEtiquetas de StructsStructs anónimosStructs anidadosValores cero y por defectoRepaso - Libreta de contactos5Composición sobre herencia
Por qué Go no tiene herenciaConceptos básicos de incrustación de structsPromoción de métodosIncrustación de múltiples structsIncrustación vs AgregaciónShadowing de métodos incrustadosResumen - Jerarquía de empleados8Manejo de Errores y OOP
La interfaz errorTipos de errores personalizadosEnvoltura de errores (fmt.Errorf)Errores centinelaerrors.Is() y errors.As()Panic, Defer y RecoverResumen - Analizador de archivos11Biblioteca estándar y POO
io.Reader e io.WriterInterfaz sort.InterfaceInterfaz fmt.Stringerencoding/json con StructsInterfaz http.HandlerRepaso - Modelos de REST API14Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositeMiddleware como Decorator3Punteros y Memoria
Fundamentos de punteros en GoPunteros a StructsPaso por valor vs referenciaLa función new()Garbage Collection en GoResumen - Constructor de Linked List6Polimorfismo en Go
Polimorfismo mediante interfacesDuck Typing en GoReglas de satisfacción de interfacesColecciones polimórficasInyección de dependenciasResumen - Procesador de pagos9Concurrencia y POO
Fundamentos de GoroutinesChannels y comunicaciónChannels con buffer vs. sin bufferSentencia Selectsync.Mutex y sync.RWMutexsync.WaitGroupDiseño de estructuras Thread-SafeResumen - Worker PoolPractica por tu cuenta: Compilador de Go online