Kihagyás

State (Állapot)

Kategória: Viselkedési minta (Behavioral Pattern)

Lényege és célja

A State minta lehetővé teszi, hogy egy objektum belső állapota megváltozásakor a viselkedése is megváltozzon – mintha "lecserélődne az osztálya". A trükk az, hogy minden állapotot külön osztályba zárunk, és a fő objektum (Context) csak egy referenciát tart az aktuális állapotra.

A működés tulajdonképpen egy objektumorientált állapotgép: az állapotátmenetek nem if-else-ek a Context-en belül, hanem maga az állapot dönti el, hogy mire vált át a következő esemény hatására.

Miben más, mint a Strategy?

A két minta szerkezete majdnem azonos (mindkettő delegál egy interfész mögé), de a szándék más:

  • Strategy: a kliens kívülről választja ki az algoritmust, és az általában nem cserélődik magától futás közben.
  • State: a Context belül tartja az állapotot, és maguk az állapotok döntenek arról, hogy mikor és mire lépjenek tovább. A kliens csak eseményeket vált ki (nextState()), és az állapotok között a kerék magától forog.

Mikor használjuk?

  • Amikor egy objektum viselkedése erősen függ a belső állapotától (pl. rendelés státusza, csomag élete: megrendelve → kiszállítás alatt → kézbesítve, kapcsolat: nyitva → fogadás → zárva).
  • Amikor egy nagy switch (state) blokk burjánzik el a kódban, és minden új állapotnál ehhez is hozzá kell nyúlni.
  • Amikor szigorúan kontrollált állapotátmenetek (state machine) kellenek, ahol egyes átmenetek érvénytelenek (pl. már kézbesített csomag nem mehet vissza kiszállítás alatti állapotba).

Mermaid Diagram

A diagramon a Context (Package) és a State interfész kapcsolata látszik. A Context delegálja a viselkedést az aktuális állapotnak, és a konkrét állapotok cserélik le a Context belső state referenciáját, amikor továbblépés történik.

classDiagram
    class Package {
        -state: PackageState
        +setState(s: PackageState) void
        +nextState() void
        +printState() void
    }

    class PackageState {
        <<interface>>
        +next(pkg: Package) void
        +printStatus() void
    }

    class OrderedState {
        +next(pkg: Package) void
        +printStatus() void
    }

    class ShippedState {
        +next(pkg: Package) void
        +printStatus() void
    }

    class DeliveredState {
        +next(pkg: Package) void
        +printStatus() void
    }

    class Client

    OrderedState ..|> PackageState : "implementálja"
    ShippedState ..|> PackageState : "implementálja"
    DeliveredState ..|> PackageState : "implementálja"

    %% A Context delegál az aktuális állapotnak
    Package o--> PackageState : "aktuális állapot (HAS-A)"

    %% Az állapotok visszaírják a Context belső state-jét
    OrderedState ..> ShippedState : "átállítja a Package-et"
    ShippedState ..> DeliveredState : "átállítja a Package-et"

    Client --> Package : "használja"

Forráskód

package behavioral.state;

interface PackageState {

    void next(Package pkg);

    void printStatus();
}

class Package {

    private PackageState state;

    public Package() {
        state = new OrderedState();
    }

    void setState(PackageState state) {
        this.state = state;
    }

    void nextState() {
        state.next(this);
    }

    void printState() {
        state.printStatus();
    }
}

class OrderedState implements PackageState {

    @Override
    public void next(Package pkg) {
        pkg.setState(new ShippedState());
    }

    @Override
    public void printStatus() {
        System.out.println("A csomag megrendelve, vár a futárra.");
    }
}

class ShippedState implements PackageState {

    @Override
    public void next(Package pkg) {
        pkg.setState(new DeliveredState());
    }

    @Override
    public void printStatus() {
        System.out.println("A csomag úton van a címzetthez.");
    }
}

class DeliveredState implements PackageState {

    @Override
    public void next(Package pkg) {
        System.out.println("A csomagot már átvették, nem léphet tovább!");
    }

    @Override
    public void printStatus() {
        System.out.println("A csomag sikeresen kézbesítve.");
    }
}

class Main {
    static void main() {
        var pkg = new Package();
        pkg.printState();

        pkg.nextState();
        pkg.printState();

        pkg.nextState();
        pkg.printState();

        pkg.nextState();
        pkg.printState();
    }
}