Menu
Coddy logo textTech

Wzorzec stanu

Część sekcji Programowanie obiektowe ścieżki Java w Coddy. Lekcja 74 z 87.

Wzorzec stanu to behawioralny wzorzec projektowy, który pozwala obiektowi zmieniać swoje zachowanie, gdy zmienia się jego stan wewnętrzny. Obiekt sprawia wrażenie, jakby zmieniał swoją klasę, ale w rzeczywistości deleguje zachowanie do różnych obiektów stanu. Pomyśl o automacie: jego reakcja na naciśnięcie przycisku zależy od tego, czy jest bezczynny, czy włożono do niego pieniądze, czy wydaje produkt.

Zamiast używać złożonych instrukcji warunkowych do obsługi różnych stanów, hermetyzujesz każdy stan w jego własnej klasie. Wzorzec obejmuje kontekst, który przechowuje odwołanie do bieżącego stanu, oraz klasy State, które definiują zachowanie dla każdego stanu:

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

Każdy konkretny stan implementuje zachowanie i może inicjować przejścia do innych stanów:

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

Kontekst przekazuje żądania do swojego bieżącego stanu, a zachowanie zmienia się automatycznie wraz ze zmianą stanu:

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

context.request();  // Bezczynny: Wrzuć monetę, aby rozpocząć
context.request();  // Aktywny: Przetwarzanie...

Wzorzec Stan sprawdza się idealnie, gdy zachowanie obiektu w dużym stopniu zależy od jego stanu i musi zmieniać się w czasie działania programu. Eliminuje rozbudowane bloki warunkowe i ułatwia dodawanie nowych stanów — wystarczy utworzyć nową klasę implementującą interfejs State.

challenge icon

Wyzwanie

Łatwy

Zbudujmy system obiegu dokumentów, używając wzorca State! Utworzysz system modelujący, jak dokument przechodzi przez różne etapy: Draft, Review i Published, przy czym każdy stan określa dostępne działania oraz zachowanie dokumentu podczas próby edycji lub zatwierdzenia.

Rozmieścisz kod w czterech plikach:

  • DocumentState.java: Zdefiniuj interfejs DocumentState, który będą implementować wszystkie stany. Powinien deklarować dwie metody: edit(Document doc) służącą do próby edycji dokumentu oraz approve(Document doc) służącą do próby zatwierdzenia dokumentu lub przejścia do następnego etapu.
  • States.java: Utwórz trzy konkretne klasy stanów implementujące DocumentState:

    DraftState — po wywołaniu edit wypisz Editing draft.... Po wywołaniu approve wypisz Draft approved. Moving to review. i przełącz dokument w stan ReviewState.

    ReviewState — po wywołaniu edit wypisz Cannot edit during review. (bez zmiany stanu). Po wywołaniu approve wypisz Review complete. Publishing document. i przełącz dokument w stan PublishedState.

    PublishedState — po wywołaniu edit wypisz Cannot edit published document.. Po wywołaniu approve wypisz Document already published.. Żadne z tych działań nie zmienia stanu.

  • Document.java: Utwórz klasę kontekstu, która przechowuje bieżący stan. Klasa Document powinna mieć:

    Prywatne pole przechowujące bieżący DocumentState, inicjalizowane w konstruktorze jako DraftState.

    Metodę setState(DocumentState state), która zmienia bieżący stan.

    Metodę edit(), która przekazuje wywołanie do metody edit bieżącego stanu.

    Metodę approve(), która przekazuje wywołanie do metody approve bieżącego stanu.

    Metodę getStatus(), która zwraca prostą nazwę klasy bieżącego stanu (użyj getClass().getSimpleName()).

  • Main.java: Zademonstruj działanie systemu stanów! Otrzymasz jedno wejście: sekwencję działań w postaci ciągu znaków rozdzielonych przecinkami, gdzie każde działanie to edit lub approve (na przykład: edit,approve,edit,approve).

    Utwórz obiekt Document i wypisz jego początkowy status w formacie Status: [state]. Następnie wykonuj po kolei każde działanie, wywołując odpowiednią metodę na dokumencie. Po każdym działaniu wypisz bieżący status.

Otrzymasz jedno wejście: ciąg działań rozdzielonych przecinkami.

Na przykład dla wejścia edit,approve,approve wynikiem będzie:

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

Zauważ, że to samo wywołanie approve() daje różne wyniki w zależności od bieżącego stanu dokumentu. Na tym polega istota wzorca State! Dokument nie używa instrukcji warunkowych do sprawdzania swojego stanu; zamiast tego przekazuje obsługę działania bieżącemu obiektowi stanu, który odpowiednio je obsługuje i w razie potrzeby zmienia stan.

Spróbuj swoich sił

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String input = scanner.nextLine();
        
        // TODO: Utwórz nowy Document
        
        // TODO: Wypisz początkowy status w formacie "Status: [state]"
        
        // TODO: Podziel dane wejściowe według przecinków, aby uzyskać poszczególne akcje
        
        // TODO: Przejdź przez każdą akcję
        // - Jeśli akcja to "edit", wywołaj edit() na document
        // - Jeśli akcja to "approve", wywołaj approve() na document
        // - Po każdej akcji wypisz bieżący status
    }
}
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator Java online