Menu
Coddy logo textTech

Template-Method-Muster

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

Das Template-Method-Muster definiert das Skelett eines Algorithmus in einem Basistyp und ermöglicht es Subtypen, 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, mittels 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

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

challenge icon

Aufgabe

Einfach

Lassen Sie uns ein Berichtserstellungssystem mit dem Template Method Pattern erstellen! Sie werden ein Framework entwickeln, 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 darstellen:

    • 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 richtigen Reihenfolge ausführt: Sammeln, Formatieren und 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!

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 Lager)
	scanner.Scan()
	configValue := scanner.Text()

	// TODO: Erstelle den passenden Generator basierend auf reportType
	// - Wenn reportType "sales" ist, erstelle einen SalesReport, wobei Region auf configValue gesetzt wird
	// - Wenn reportType "inventory" ist, erstelle einen InventoryReport, wobei Warehouse auf configValue gesetzt wird
	
	// TODO: Erstelle einen ReportProcessor mit dem Generator
	
	// TODO: Rufe Generate() auf und gib 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