Menu
Coddy logo textTech

State-Muster

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

Das State-Muster ermöglicht es einem Objekt, sein Verhalten zu ändern, wenn sich sein interner Zustand ändert, sodass es den Anschein erweckt, als hätte das Objekt seine Klasse geändert. Während die Template Method die Schritte eines Algorithmus steuert, kapselt State das zustandsspezifische Verhalten in separate Objekte und delegiert an den aktuellen Zustand.

In Go definieren wir ein Zustandsinterface und erstellen konkrete Zustände, die unterschiedliche Verhaltensweisen implementieren:

type State interface {
    Handle(d *Document) string
}

type Document struct {
    state State
}

func (d *Document) SetState(s State) {
    d.state = s
}

func (d *Document) Publish() string {
    return d.state.Handle(d)
}

Jeder Zustand bestimmt, was passiert und welcher Zustand als Nächstes folgt:

type DraftState struct{}

func (s DraftState) Handle(d *Document) string {
    d.SetState(ModerationState{})
    return "Draft submitted for moderation"
}

type ModerationState struct{}

func (s ModerationState) Handle(d *Document) string {
    d.SetState(PublishedState{})
    return "Moderation approved, now published"
}

type PublishedState struct{}

func (s PublishedState) Handle(d *Document) string {
    return "Already published"
}

Derselbe Methodenaufruf erzeugt je nach aktuellem Zustand unterschiedliche Ergebnisse:

doc := &Document{state: DraftState{}}

fmt.Println(doc.Publish())  // Entwurf zur Moderation eingereicht
fmt.Println(doc.Publish())  // Moderation genehmigt, jetzt veröffentlicht
fmt.Println(doc.Publish())  // Bereits veröffentlicht

State eignet sich ideal für Objekte mit klar voneinander abgegrenzten Betriebsmodi, etwa für Workflows zur Bestellverarbeitung, UI-Komponenten oder Verbindungs-Handler, bei denen das Verhalten vollständig vom aktuellen Zustand abhängt.

challenge icon

Aufgabe

Einfach

Erstellen wir ein Ticket-Supportsystem unter Verwendung des State-Patterns! Du erstellst ein Supportticket, das verschiedene Phasen durchläuft: vom Öffnen über die Bearbeitung bis zur Lösung. Dabei bestimmt jeder Zustand, welche Aktionen möglich sind und was als Nächstes geschieht.

Du organisierst deinen Code auf drei Dateien verteilt:

  • state.go: Define deine State-Schnittstelle und die konkreten Zustände, die die einzelnen Phasen des Lebenszyklus eines Tickets darstellen.

    Erstelle eine TicketState-Schnittstelle mit einer Methode Handle(t *Ticket) string, die das Ticket verarbeitet und es möglicherweise in den nächsten Zustand überführt.

    Implementiere drei Zustände:

    • OpenState: Bei der Verarbeitung wird das Ticket in InProgressState überführt und Ticket opened, assigning to support team zurückgegeben
    • InProgressState: Bei der Verarbeitung erfolgt der Übergang zu ResolvedState und Working on ticket, issue resolved wird zurückgegeben
    • ResolvedState: Bei der Verarbeitung bleibt das Ticket im selben Zustand und Ticket already resolved wird zurückgegeben
  • ticket.go: Erstelle deine Ticketstruktur, die den aktuellen Zustand enthält und ihr behavior an diesen delegiert.

    Erstelle eine Ticket-Struktur mit einem ID-field (string) und einem state-field (TicketState). Füge diese Methoden hinzu:

    • SetState(s TicketState), um den aktuellen Zustand des Tickets zu ändern
    • Process() string, die an die Handle-Methode des aktuellen Zustands delegiert

    Erstelle einen NewTicket(id string) *Ticket-constructor, der ein Ticket zurückgibt, das mit dem OpenState beginnt.

  • main.go: Zeige, wie dieselbe Aktion abhängig vom aktuellen Zustand des Tickets unterschiedliche results erzeugt.

    Lies eine Ticket-ID und die Anzahl der Verarbeitungen des Tickets ein. Erstelle ein neues Ticket mit dieser ID und rufe anschließend Process() so oft wie angegeben auf, wobei jedes result in einer eigenen Zeile ausgegeben wird.

Die folgenden Eingaben werden bereitgestellt:

  • Zeile 1: Ticket-ID
  • Zeile 2: Anzahl der Verarbeitungen des Tickets

Beispiel für folgende Eingabe:

TKT-001
3

Deine Ausgabe sollte wie folgt aussehen:

Ticket opened, assigning to support team
Working on ticket, issue resolved
Ticket already resolved

Beispiel für folgende Eingabe:

TKT-500
5

Deine Ausgabe sollte wie folgt aussehen:

Ticket opened, assigning to support team
Working on ticket, issue resolved
Ticket already resolved
Ticket already resolved
Ticket already resolved

Beispiel für folgende Eingabe:

ISSUE-42
1

Deine Ausgabe sollte wie folgt aussehen:

Ticket opened, assigning to support team

Beachte, wie der Aufruf von Process() für dasselbe Ticket jedes Mal unterschiedliche results erzeugt: Das behavior des Tickets changes, während es die Zustände durchläuft. Sobald es resolved ist, bleibt es resolved, unabhängig davon, wie oft du es verarbeitest. Das Ticketobjekt scheint sein behavior zu ändern, delegiert aber tatsächlich an unterschiedliche Zustandsobjekte!

Probier es selbst

package main

import (
	"fmt"
)

func main() {
	// Eingabe lesen
	var ticketID string
	var numProcesses int
	fmt.Scanln(&ticketID)
	fmt.Scanln(&numProcesses)

	// TODO: Erstelle ein neues Ticket mit der gegebenen ID

	// TODO: Verarbeite das Ticket die angegebene Anzahl von Malen
	// und gib jedes Ergebnis in einer separaten Zeile aus
}
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

Übe selbstständig: Online-Go-Compiler