Patron Template Method
Fait partie de la section Programmation Orientée Objet du Journey GO de Coddy — leçon 94 sur 107.
Le patron Template Method définit le squelette d'un algorithme dans un type de base, permettant aux sous-types de redéfinir des étapes spécifiques sans modifier la structure globale. Alors que le Decorator ajoute du comportement en enveloppant des objets, Template Method contrôle le flux de l'algorithme et permet la personnalisation d'étapes individuelles.
En Go, comme il n'y a pas d'héritage, nous implémentons ce patron en utilisant l'imbrication de structures (struct embedding) combinée aux interfaces. La structure « template » définit la structure de l'algorithme et appelle des méthodes qui peuvent être personnalisées :
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)
}Les implémentations concrètes fournissent leurs propres versions de chaque étape tandis que le flux de l'algorithme reste inchangé :
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 }La méthode template Execute orchestre les étapes dans un ordre fixe :
csvProc := &BaseProcessor{Impl: CSVProcessor{}}
fmt.Println(csvProc.Execute()) // Enregistré : processed-csv-data
jsonProc := &BaseProcessor{Impl: JSONProcessor{}}
fmt.Println(jsonProc.Execute()) // Stocké : parsed-json-dataLe Template Method est idéal lorsque vous avez des algorithmes qui partagent la même structure mais diffèrent par des étapes spécifiques, tels que des pipelines d'importation/exportation de données, des générateurs de rapports ou des frameworks de test.
Défi
FacileConstruisons un système de génération de rapports en utilisant le patron de conception Template Method ! Vous allez créer un framework où différents types de rapports suivent le même processus de génération — collecte des données, formatage et sortie du résultat — mais chaque type de rapport personnalise ces étapes différemment.
Vous organiserez votre code sur trois fichiers :
report.go: Définissez votre interface et le processeur de base qui orchestre l'algorithme de génération de rapports.Créez une interface
ReportGeneratoravec trois méthodes qui représentent les étapes de la génération de rapports :GatherData() string— récupère les données brutes pour le rapportFormatData(data string) string— transforme les données dans le format du rapportOutputReport(formatted string) string— produit le message de sortie final
Construisez une structure
ReportProcessorqui contient une implémentation deReportGenerator. Ajoutez une méthodeGenerate() stringqui exécute les trois étapes dans l'ordre : collecter, formater, puis sortir — en retournant le résultat final.generators.go: Implémentez des générateurs de rapports concrets qui personnalisent chaque étape.Créez deux types de rapports :
SalesReportavec un champRegion(string)GatherData()retourne"sales-data-"+ la régionFormatData(data)retourne"SALES REPORT: "+ les donnéesOutputReport(formatted)retourne"Printed: "+ le contenu formaté
InventoryReportavec un champWarehouse(string)GatherData()retourne"inventory-"+ l'entrepôtFormatData(data)retourne"*** "+ les données +" ***"OutputReport(formatted)retourne"Exported: "+ le contenu formaté
main.go: Démontrez comment la même structure d'algorithme produit des résultats différents selon l'implémentation.Lisez le type de rapport (
salesouinventory) et la valeur de configuration (région pour les ventes, entrepôt pour l'inventaire). Créez le générateur approprié, enveloppez-le dans unReportProcessor, appelezGenerate(), et affichez le résultat.
Les entrées suivantes seront fournies :
- Ligne 1 : Type de rapport (
salesouinventory) - Ligne 2 : Valeur de configuration (nom de la région ou nom de l'entrepôt)
Par exemple, avec :
sales
NorthVotre sortie devrait être :
Printed: SALES REPORT: sales-data-NorthEt avec :
inventory
MainHubVotre sortie devrait être :
Exported: *** inventory-MainHub ***Et avec :
sales
WestVotre sortie devrait être :
Printed: SALES REPORT: sales-data-WestRemarquez comment le ReportProcessor appelle toujours les trois mêmes étapes dans le même ordre, mais chaque type de rapport fournit sa propre implémentation de ces étapes. Le squelette de l'algorithme reste fixe tandis que les détails varient — c'est le patron Template Method en action !
Aide-mémoire
Le patron de méthode (Template Method) définit le squelette d'un algorithme dans un type de base, permettant aux sous-types de surcharger des étapes spécifiques sans modifier la structure globale.
En Go, implémentez ce patron en utilisant l'imbrication de structures (struct embedding) combinée aux interfaces. La structure « template » définit la structure de l'algorithme et appelle des méthodes qui peuvent être personnalisées :
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)
}Les implémentations concrètes fournissent leurs propres versions de chaque étape tandis que le flux de l'algorithme reste inchangé :
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 }La méthode template orchestre les étapes dans un ordre fixe :
csvProc := &BaseProcessor{Impl: CSVProcessor{}}
fmt.Println(csvProc.Execute()) // Saved: processed-csv-data
jsonProc := &BaseProcessor{Impl: JSONProcessor{}}
fmt.Println(jsonProc.Execute()) // Stored: parsed-json-dataLe patron Template Method est idéal lorsque des algorithmes partagent la même structure mais diffèrent dans des étapes spécifiques, comme les pipelines d'importation/exportation de données, les générateurs de rapports ou les frameworks de test.
Essayez vous-même
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Lire le type de rapport
scanner.Scan()
reportType := scanner.Text()
// Lire la valeur de configuration (région ou entrepôt)
scanner.Scan()
configValue := scanner.Text()
// TODO: Créer le générateur approprié en fonction de reportType
// - Si reportType est "sales", créer un SalesReport avec Region défini sur configValue
// - Si reportType est "inventory", créer un InventoryReport avec Warehouse défini sur configValue
// TODO: Créer un ReportProcessor avec le générateur
// TODO: Appeler Generate() et afficher le résultat
_ = reportType
_ = configValue
fmt.Println("TODO: Generate and print the report")
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation Orientée Objet
1Fondamentaux de la POO en Go
Fichiers externesEspace de travail et Modules GoPackages et ImportsNoms exportés vs non exportésIntroduction à la POO en GoLes Structs comme ClassesDéfinir des méthodes sur les StructsReceveurs par pointeur vs par valeurInitialisation de StructsFonctions constructeursRécapitulatif - Calculatrice simple4Interfaces
Introduction aux interfacesImplémentation impliciteL'interface comme contratInterface vide (any)Assertion de typeType SwitchComposition d'interfacesInterfaces Stringer & ErrorRécapitulatif - Calculateur de formes7Encapsulation
Champs exportés vs non exportésEncapsulation au niveau du packageMéthodes Getter & SetterMasquage d'informations en GoRécapitulatif - Dossiers étudiants10Génériques (Go 1.18+)
Introduction aux génériquesParamètres de typeContraintes de typeStructures génériquesContournement pour les méthodes génériquesRécapitulatif - Collections génériques13Patrons de conception, Partie 1
Introduction aux Design PatternsPatron SingletonPatron FactoryPatron Abstract FactoryPatron ObserverPatron Strategy2Exploration approfondie des Types et Structs
Types de base et compositesDéfinitions de types personnalisésTags de StructStructs anonymesStructs imbriquéesValeurs par défaut et Zero ValuesRécapitulatif - Carnet de contacts5Composition plutôt qu'héritage
Pourquoi Go n'a pas d'héritageLes bases de l'imbrication de structsPromotion de méthodesImbriquer plusieurs structsImbrication vs AgrégationMasquage de méthodes imbriquéesRécapitulatif - Hiérarchie des employés8Gestion des erreurs & POO
L'interface errorTypes d'erreurs personnalisésError Wrapping (fmt.Errorf)Erreurs sentinelleserrors.Is() et errors.As()Panic, Defer et RecoverRécapitulatif - File Parser11Bibliothèque standard et POO
io.Reader & io.WriterL'interface sort.InterfaceL'interface fmt.Stringerencoding/json avec les StructsL'interface http.HandlerRécapitulatif - Modèles d'API REST14Patrons de conception, Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Template MethodPatron ÉtatPatron CompositeMiddleware en tant que Décorateur3Pointeurs et Mémoire
Bases des pointeurs en GoPointeurs vers des structsPassage par valeur vs référenceLa fonction new()Garbage Collection en GoRécapitulatif - Constructeur de liste chaînée6Le polymorphisme en Go
Le polymorphisme via les interfacesLe Duck Typing en GoRègles de satisfaction des interfacesCollections polymorphesInjection de dépendancesRécapitulatif - Processeur de paiement9Concurrence et POO
Bases des GoroutinesChannels et CommunicationChannels avec buffer vs sans bufferL'instruction Selectsync.Mutex et sync.RWMutexsync.WaitGroupConception de structures Thread-SafeRécapitulatif - Worker Pool