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