Menu
Coddy logo textTech

Zustandsmuster

Teil des Abschnitts Objektorientierte Programmierung der Java-Journey von Coddy. Lektion 74 von 87.

Das State Pattern ist ein Verhaltensentwurfsmuster, das einem Objekt ermöglicht, sein Verhalten zu ändern, wenn sich sein interner Zustand ändert. Das Objekt scheint seine Klasse zu ändern, tatsächlich delegiert es sein Verhalten jedoch an verschiedene Zustandsobjekte. Stell dir einen Verkaufsautomaten vor: Seine Reaktion auf das Drücken einer Taste hängt davon ab, ob er im Leerlauf ist, Geld eingeworfen wurde oder er ein Produkt ausgibt.

Anstatt komplexe bedingte Anweisungen zur Behandlung verschiedener Zustände zu verwenden, kapselst du jeden Zustand in einer eigenen Klasse. Das Muster umfasst einen Kontext, der eine Referenz auf den aktuellen Zustand verwaltet, sowie Zustandsklassen, die das Verhalten für jeden Zustand definieren:

interface State {
    void handle(Context context);
}

class Context {
    private State state;
    
    public void setState(State state) {
        this.state = state;
    }
    
    public void request() {
        state.handle(this);
    }
}

Jeder konkrete Zustand implementiert das Verhalten und kann Übergänge zu anderen Zuständen auslösen:

class IdleState implements State {
    public void handle(Context context) {
        System.out.println("Idle: Insert coin to start");
        context.setState(new ActiveState());
    }
}

class ActiveState implements State {
    public void handle(Context context) {
        System.out.println("Active: Processing...");
        context.setState(new IdleState());
    }
}

Der Kontext delegiert Anfragen an seinen aktuellen Zustand, und das Verhalten ändert sich automatisch, wenn sich der Zustand ändert:

Context context = new Context();
context.setState(new IdleState());

context.request();  // Idle: Münze einwerfen zum Starten
context.request();  // Active: Verarbeitung...

Das Zustandsmuster ist ideal, wenn das Verhalten eines Objekts stark von seinem Zustand abhängt und sich zur Laufzeit ändern muss. Es beseitigt umfangreiche bedingte Blöcke und macht das Hinzufügen neuer Zustände unkompliziert: Erstelle einfach eine neue Klasse, die das State-Interface implementiert.

challenge icon

Aufgabe

Einfach

Erstellen wir ein Dokument-Workflow-System mit dem Zustandsmuster! Du erstellst ein System, das modelliert, wie ein Dokument verschiedene Phasen durchläuft: Draft, Review und Published. Dabei bestimmt jeder Zustand, welche Aktionen verfügbar sind und wie sich das Dokument verhält, wenn du versuchst, es zu bearbeiten oder zu genehmigen.

Du wirst deinen Code auf vier Dateien verteilen:

  • DocumentState.java: Definiere das Interface DocumentState, das alle Zustände implementieren werden. Es soll zwei Methoden deklarieren: edit(Document doc) zum Versuch, das Dokument zu bearbeiten, und approve(Document doc) zum Versuch, das Dokument zu genehmigen bzw. in die nächste Phase zu überführen.
  • States.java: Erstelle drei konkrete Zustandsklassen, die DocumentState implementieren:

    DraftState – Wenn edit aufgerufen wird, gib Editing draft... aus. Wenn approve aufgerufen wird, gib Draft approved. Moving to review. aus und überführe das Dokument in den Zustand ReviewState.

    ReviewState – Wenn edit aufgerufen wird, gib Cannot edit during review. aus (keine Zustandsänderung). Wenn approve aufgerufen wird, gib Review complete. Publishing document. aus und überführe das Dokument in den Zustand PublishedState.

    PublishedState – Wenn edit aufgerufen wird, gib Cannot edit published document. aus. Wenn approve aufgerufen wird, gib Document already published. aus. Keine der beiden Aktionen ändert den Zustand.

  • Document.java: Erstelle die Kontextklasse, die den aktuellen Zustand verwaltet. Dein Document soll Folgendes enthalten:

    Ein privates Feld, das den aktuellen DocumentState enthält und im Konstruktor mit DraftState initialisiert wird.

    Eine Methode setState(DocumentState state), die den aktuellen Zustand ändert.

    Eine Methode edit(), die die Methode edit des aktuellen Zustands aufruft.

    Eine Methode approve(), die die Methode approve des aktuellen Zustands aufruft.

    Eine Methode getStatus(), die den einfachen Klassennamen des aktuellen Zustands zurückgibt (verwende getClass().getSimpleName()).

  • Main.java: Führe dein Zustandssystem vor! Du erhältst eine Eingabe: eine Folge von Aktionen als durch Kommas getrennte Zeichenkette, wobei jede Aktion entweder edit oder approve ist (zum Beispiel: edit,approve,edit,approve).

    Erstelle ein Document und gib seinen anfänglichen Status im Format Status: [state] aus. Verarbeite anschließend jede Aktion der Reihe nach und rufe die entsprechende Methode für das Dokument auf. Gib nach jeder Aktion den aktuellen Status aus.

Du erhältst eine Eingabe: eine durch Kommas getrennte Zeichenkette von Aktionen.

Bei der Eingabe edit,approve,approve würde deine Ausgabe beispielsweise so aussehen:

Status: DraftState
Editing draft...
Status: DraftState
Draft approved. Moving to review.
Status: ReviewState
Review complete. Publishing document.
Status: PublishedState

Beachte, wie derselbe Aufruf approve() abhängig vom aktuellen Zustand des Dokuments unterschiedliche Ergebnisse erzeugt. Das ist der Kern des Zustandsmusters! Das Dokument verwendet keine Bedingungen, um seinen Zustand zu überprüfen. Stattdessen delegiert es das Verhalten an das aktuelle Zustandsobjekt, das die Aktion angemessen verarbeitet und bei Bedarf Übergänge auslöst.

Probier es selbst

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String input = scanner.nextLine();
        
        // TODO: Erstelle ein neues Document
        
        // TODO: Gib den initialen Status im Format "Status: [state]" aus
        
        // TODO: Teile die Eingabe am Komma auf, um einzelne Aktionen zu erhalten
        
        // TODO: Durchlaufe jede Aktion
        // - Wenn die Aktion "edit" ist, rufe edit() auf document auf
        // - Wenn die Aktion "approve" ist, rufe approve() auf document auf
        // - Nach jeder Aktion den aktuellen Status ausgeben
    }
}
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-Java-Compiler