Menu
Coddy logo textTech

Template-Method-Muster

Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy — Lektion 94 von 107.

Das Template Method Pattern definiert das Skelett eines Algorithmus in einem Basistyp und ermöglicht es Untertypen, bestimmte Schritte zu überschreiben, ohne die Gesamtstruktur zu ändern. Während der Decorator Verhalten durch das Einpacken von Objekten hinzufügt, steuert die Template Method den Ablauf des Algorithmus und ermöglicht die Anpassung einzelner Schritte.

In Go implementieren wir dieses Muster, da es keine Vererbung gibt, mithilfe von Struct-Embedding in Kombination mit Interfaces. Das „Template“-Struct definiert die Algorithmusstruktur und ruft Methoden auf, die angepasst werden können:

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)
}

Konkrete Implementierungen stellen ihre eigenen Versionen für jeden Schritt bereit, während der Algorithmus-Ablauf unverändert bleibt:

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 }

Die Template-Methode Execute orchestriert die Schritte in einer festen Reihenfolge:

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

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

Die Template-Methode ist ideal, wenn Sie Algorithmen haben, die dieselbe Struktur teilen, sich aber in spezifischen Schritten unterscheiden, wie zum Beispiel Datenimport/-export-Pipelines, Berichtgeneratoren oder Test-Frameworks.

challenge icon

Aufgabe

Einfach

Lassen wir uns ein Berichtserstellungssystem mit dem Template Method Pattern bauen! Sie werden ein Framework erstellen, in dem verschiedene Berichtstypen demselben Erstellungsprozess folgen – Daten sammeln, formatieren und das Ergebnis ausgeben – aber jeder Berichtstyp diese Schritte unterschiedlich anpasst.

Sie werden Ihren Code auf drei Dateien verteilen:

  • report.go: Definieren Sie Ihr Interface und den Basis-Prozessor, der den Algorithmus zur Berichtserstellung orchestriert.

    Erstellen Sie ein ReportGenerator-Interface mit drei Methoden, die die Schritte der Berichtserstellung repräsentieren:

    • GatherData() string — ruft die Rohdaten für den Bericht ab
    • FormatData(data string) string — transformiert die Daten in das Format des Berichts
    • OutputReport(formatted string) string — erzeugt die endgültige Ausgabemeldung

    Erstellen Sie eine ReportProcessor-Struktur, die eine ReportGenerator-Implementierung enthält. Fügen Sie eine Generate() string-Methode hinzu, die die drei Schritte in der Reihenfolge ausführt: Sammeln, Formatieren, dann Ausgeben – und das Endergebnis zurückgibt.

  • generators.go: Implementieren Sie konkrete Berichtsgeneratoren, die jeden Schritt anpassen.

    Erstellen Sie zwei Berichtstypen:

    • SalesReport mit einem Region-Feld (string)
      • GatherData() gibt "sales-data-" + Region zurück
      • FormatData(data) gibt "SALES REPORT: " + data zurück
      • OutputReport(formatted) gibt "Printed: " + formatted zurück
    • InventoryReport mit einem Warehouse-Feld (string)
      • GatherData() gibt "inventory-" + Warehouse zurück
      • FormatData(data) gibt "*** " + data + " ***" zurück
      • OutputReport(formatted) gibt "Exported: " + formatted zurück
  • main.go: Demonstrieren Sie, wie dieselbe Algorithmusstruktur je nach Implementierung unterschiedliche Ergebnisse liefert.

    Lesen Sie den Berichtstyp ("sales" oder "inventory") und den Konfigurationswert (Region für Sales, Lagerhaus für Inventory) ein. Erstellen Sie den entsprechenden Generator, betten Sie ihn in einen ReportProcessor ein, rufen Sie Generate() auf und geben Sie das Ergebnis aus.

Die folgenden Eingaben werden bereitgestellt:

  • Zeile 1: Berichtstyp ("sales" oder "inventory")
  • Zeile 2: Konfigurationswert (Regionsname oder Lagerhausname)

Zum Beispiel, gegeben:

sales
North

Sollte Ihre Ausgabe wie folgt aussehen:

Printed: SALES REPORT: sales-data-North

Und gegeben:

inventory
MainHub

Sollte Ihre Ausgabe wie folgt aussehen:

Exported: *** inventory-MainHub ***

Und gegeben:

sales
West

Sollte Ihre Ausgabe wie folgt aussehen:

Printed: SALES REPORT: sales-data-West

Beachten Sie, wie der ReportProcessor immer dieselben drei Schritte in derselben Reihenfolge aufruft, aber jeder Berichtstyp seine eigene Implementierung dieser Schritte bereitstellt. Das Skelett des Algorithmus bleibt fest, während die Details variieren – das ist das Template Method Pattern in Aktion!

Spickzettel

Das Template Method Pattern (Schablonenmethode) definiert das Skelett eines Algorithmus in einem Basistyp und ermöglicht es Untertypen, spezifische Schritte zu überschreiben, ohne die Gesamtstruktur zu ändern.

In Go implementieren Sie dieses Muster mithilfe von Struct-Embedding in Kombination mit Interfaces. Das „Template“-Struct definiert die Algorithmusstruktur und ruft Methoden auf, die angepasst werden können:

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)
}

Konkrete Implementierungen stellen ihre eigenen Versionen jedes Schritts bereit, während der Algorithmusablauf unverändert bleibt:

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 }

Die Template-Methode orchestriert die Schritte in einer festen Reihenfolge:

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

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

Das Template Method Pattern ist ideal, wenn Algorithmen dieselbe Struktur teilen, sich aber in spezifischen Schritten unterscheiden, wie z. B. bei Datenimport/-export-Pipelines, Berichtgeneratoren oder Test-Frameworks.

Probier es selbst

package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	// Berichtstyp lesen
	scanner.Scan()
	reportType := scanner.Text()
	
	// Konfigurationswert lesen (Region oder Lagerhaus)
	scanner.Scan()
	configValue := scanner.Text()

	// TODO: Erstellen Sie den entsprechenden Generator basierend auf reportType
	// - Wenn reportType "sales" ist, erstellen Sie einen SalesReport mit Region gesetzt auf configValue
	// - Wenn reportType "inventory" ist, erstellen Sie einen InventoryReport mit Warehouse gesetzt auf configValue
	
	// TODO: Erstellen Sie einen ReportProcessor mit dem Generator
	
	// TODO: Rufen Sie Generate() auf und geben Sie das Ergebnis aus
	_ = reportType
	_ = configValue
	fmt.Println("TODO: Generate and print the report")
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung