Pattern Template Method
Fa parte della sezione Programmazione orientata agli oggetti del percorso GO di Coddy. Lezione 94 di 107.
Il pattern Template Method definisce lo scheletro di un algoritmo in un tipo base, consentendo ai sottotipi di sovrascrivere passaggi specifici senza modificare la struttura complessiva. Mentre Decorator aggiunge comportamenti avvolgendo gli oggetti, Template Method controlla il flusso dell’algoritmo e consente di personalizzare i singoli passaggi.
In Go, poiché non esiste l'ereditarietà, implementiamo questo schema usando l'incorporamento di struct insieme alle interfacce. La struct "template" definisce la struttura dell'algoritmo e chiama metodi che possono essere personalizzati:
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)
}Le implementazioni concrete forniscono le proprie versioni di ogni passaggio, mentre il flusso dell’algoritmo rimane invariato:
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 }Il metodo modello Execute orchestra i passaggi in un ordine fisso:
csvProc := &BaseProcessor{Impl: CSVProcessor{}}
fmt.Println(csvProc.Execute()) // Salvato: processed-csv-data
jsonProc := &BaseProcessor{Impl: JSONProcessor{}}
fmt.Println(jsonProc.Execute()) // Archiviato: parsed-json-dataIl Metodo Template è ideale quando hai algoritmi che condividono la stessa struttura ma differiscono in passaggi specifici, come pipeline di importazione/esportazione dei dati, generatori di report o framework di test.
Sfida
FacileCostruiamo un sistema di generazione di report usando il pattern Template Method! Creerai un framework in cui diversi tipi di report seguono lo stesso processo di generazione, raccogliendo i dati, formattandoli e producendo il risultato, ma ogni tipo di report personalizza questi passaggi in modo diverso.
Organizzerai il codice in tre file:
report.go: definisci l'interfaccia e il processore base che orchestra l'algoritmo di generazione dei report.Crea un'interfaccia
ReportGeneratorcon tre metodi che rappresentano i passaggi della generazione di un report:GatherData() string: recupera i dati grezzi per il reportFormatData(data string) string: trasforma i dati nel formato del reportOutputReport(formatted string) string: produce il messaggio di output finale
Crea una struct
ReportProcessorche contiene un'implementazione diReportGenerator. Aggiungi un metodoGenerate() stringche esegue i tre passaggi nell'ordine: raccogliere, formattare e poi produrre l'output, restituendo il risultato finale.generators.go: implementa generatori concreti di report che personalizzano ogni passaggio.Crea due tipi di report:
SalesReportcon un campoRegion(stringa)GatherData()restituiscesales-data-[region]FormatData(data)restituisceSALES REPORT: [data]OutputReport(formatted)restituiscePrinted: [formatted]
InventoryReportcon un campoWarehouse(stringa)GatherData()restituisceinventory-[warehouse]FormatData(data)restituisce*** [data] ***OutputReport(formatted)restituisceExported: [formatted]
main.go: mostra come la stessa struttura dell'algoritmo produca risultati diversi in base all'implementazione.Leggi il tipo di report (
salesoinventory) e il valore di configurazione (la regione per le vendite, il magazzino per l'inventario). Crea il generatore appropriato, inseriscilo in unReportProcessor, chiamaGenerate()e stampa il risultato.
Verranno forniti i seguenti input:
- Riga 1: tipo di report (
salesoinventory) - Riga 2: valore di configurazione (nome della regione o del magazzino)
Per esempio, dato:
sales
NorthL'output dovrebbe essere:
Printed: SALES REPORT: sales-data-NorthE dato:
inventory
MainHubL'output dovrebbe essere:
Exported: *** inventory-MainHub ***E dato:
sales
WestL'output dovrebbe essere:
Printed: SALES REPORT: sales-data-WestNota come ReportProcessor chiami sempre gli stessi tre passaggi nello stesso ordine, ma ogni tipo di report fornisca una propria implementazione di questi passaggi. Lo scheletro dell'algoritmo resta fisso mentre i dettagli variano: è il pattern Template Method in azione!
Provalo tu
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Leggi il tipo di report
scanner.Scan()
reportType := scanner.Text()
// Leggi il valore di configurazione (regione o magazzino)
scanner.Scan()
configValue := scanner.Text()
// TODO: Crea il generatore appropriato in base a reportType
// - Se reportType è "sales", crea un SalesReport con Region impostato su configValue
// - Se reportType è "inventory", crea un InventoryReport con Warehouse impostato su configValue
// TODO: Crea un ReportProcessor con il generatore
// TODO: Chiama Generate() e stampa il risultato
_ = reportType
_ = configValue
fmt.Println("TODO: Generate and print the report")
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti di OOP in Go
File esterniWorkspace e moduli GoPacchetti e importazioniNomi esportati e non esportatiIntroduzione all’OOP in GoGli struct come classiDefinire metodi sugli structReceiver con puntatore vs receiver con valoreInizializzazione degli structFunzioni costruttriciRiepilogo - Calcolatrice semplice4Interfacce
Introduzione alle interfacceImplementazione implicitaL’interfaccia come contrattoInterfaccia vuota (any)Asserzione di tipoSwitch sul tipoComposizione di interfacceInterfacce Stringer ed ErrorRipasso - Calcolatore di forme7Incapsulamento
Campi esportati e non esportatiIncapsulamento a livello di packageMetodi Getter e SetterNascondimento delle informazioni in GoRiepilogo - Registri degli studenti10Tipi generici (Go 1.18+)
Introduzione ai tipi genericiParametri di tipoVincoli di tipoStruct genericheSoluzione alternativa per i metodi genericiRiepilogo - Collezione generica13Modelli di progettazione Parte 1
Introduzione ai modelli di progettazioneModello SingletonModello FactoryModello Abstract FactoryModello ObserverModello Strategy2Approfondimento su tipi e struct
Tipi di base e compostiDefinizioni di tipi personalizzatiTag delle structStruct anonimeStruct annidateValori zero e predefinitiRipasso - Rubrica5Composizione invece dell'ereditarietà
Perché Go non ha l'ereditarietàFondamenti dell'embedding delle structPromozione dei metodiEmbedding di più structEmbedding e aggregazione a confrontoMascheramento dei metodi embeddedRiepilogo: gerarchia dei dipendenti8Gestione degli errori e OOP
L'interfaccia errorTipi di errore personalizzatiWrapping degli errori (fmt.Errorf)Errori sentinellaerrors.Is() ed errors.As()Panic, Defer e RecoverRiepilogo - Parser di file11Libreria standard e OOP
io.Reader e io.Writersort.InterfaceInterfaccia fmt.Stringerencoding/json con le structInterfaccia http.HandlerRipasso - modelli REST API14Modelli di progettazione – Parte 2
Pattern CommandPattern AdapterPattern DecoratorPattern Template MethodPattern StatePattern CompositeMiddleware come Decorator3Puntatori e memoria
Concetti base dei puntatori in GoPuntatori a structPassaggio per valore vs per riferimentoLa funzione new()Garbage collection in GoRiepilogo - Costruttore di liste concatenate6Polimorfismo in Go
Polimorfismo tramite interfacceDuck typing in GoRegole di soddisfacimento delle interfacceCollezioni polimorficheIniezione delle dipendenzeRiepilogo - Elaboratore di pagamenti9Concorrenza e OOP
Basi delle goroutineCanali e comunicazioneCanali bufferizzati vs non bufferizzatiIstruzione selectsync.Mutex e sync.RWMutexsync.WaitGroupProgettazione di struct thread-safeRiepilogo: worker poolEsercitati da solo: Compilatore Go online