Állapot programtervezési minta
Az állapot programtervezési minta nagyon hasonlít a stratégia mintára. Ez egy viselkedési programtervezési minta, amit angolul object for state patternként is hívnak. Ez a programtervezési minta egységbe zárja az egy azon rutinhoz használt különböző viselkedéseket alapul véve az objektum állapotát. Feltételes utasítások tömege nélkül, letisztultabban képes az objektum viselkedésének megváltoztatására futási időben.[1]
Áttekintés
[szerkesztés]Az állapot programtervezési minta egy Gammáék 23 programtervezési mintája közül, melyek leírják, hogyan lehet megoldani az ismétlődő tervezési problémákat. Az ilyen problémák kiterjednek a rugalmas és újrafelhasználható objektumorientált szoftver tervezésre, például olyan objektumok kialakítására, amelyek könnyen megvalósíthatók, megváltoztathatók, tesztelhetők és újra felhasználhatók.
Az állapottervezési minta két fő probléma megoldására alkalmas:
- Ha egy objektum belső állapota megváltozik, akkor a viselkedésének is meg kell változnia.
- Az állapot-specifikus viselkedést függetlenül kell megvalósítani. Vagyis az új állapotok felvétele nem befolyásolhatja a meglévők viselkedését.
Az állapot-specifikus viselkedés közvetlenül az osztályon belüli implementálása rugalmatlan, mivel arra kötelezi az osztályt, hogy egy adott módon viselkedjen és lehetetlenné teszi az új állapotok hozzáadását vagy a meglévő állapot viselkedésének megváltoztatását később az osztálytól függetlenül.
Ehhez a minta kettő megoldást ír le:
- Különálló (állapot) objektumok definiálása, amelyek az egyes állapot-specifikus viselkedéseket zárják egységbe. Egy (állapot) interfészt definiál az állapot-specifikus viselkedés végrehajtásához és meghatározza azokat az osztályokat, melyek implementálják az interfészt minden egyes állapothoz.
- Egy osztály az állapot-specifikus viselkedést delegálja a jelenlegi állapotobjektumára, ahelyett, hogy közvetlenül végrehajtaná az állapot-specifikus viselkedést.
Ez az osztálytól függetlenné teszi az állapot-specifikus viselkedés megvalósítását. Új állapotok hozzáadhatók új állapotosztályok meghatározásával. Egy osztály megváltoztathatja viselkedését futási időben az aktuális állapot objektumának megváltoztatásával.
Szerkezet
[szerkesztés]Példa
[szerkesztés]Pszeudokód
[szerkesztés]Vegyünk példának egy rajzolóprogramot. A programnak van egy egérkurzora, ami bármikor bármelyik ponton többféle eszközként is működhet. Ahelyett, hogy több kurzor objektum közt váltogatnánk, a kurzor rendelkezik egy belső állapottal, ami az aktuális eszközt képviseli. Amikor egy eszköz függő metódus meghívásra kerül (mondjuk egy egérkattintás hatására), a metódushívás átadódik a kurzor állapotára.
Minden eszköz egy állapotnak felel meg. A megosztott absztrakt állapot az AbsztraktEszköz.
osztály AbsztraktEszköz Mozgatás(pont) bemenet: A pont, ahova az egér elmozdul (ezt a függvényt a gyermekosztályokban kell implementálni) függvény egérLe(pont) bemenet: A pont, ahol az egér van (ezt a függvényt a gyermekosztályokban kell implementálni) függvény egérFel(pont) bemenet: A pont, ahol az egér van (ezt a függvényt a gyermekosztályokban kell implementálni)
E szerint a definíció szerint minden eszköznek kezelnie kell az egér kurzor mozgását és a kattintások vagy húzások kezdetét és végét.
Ezt az alap osztályt használva az egyszerű toll és kijelölő eszközök valahogy így néznek ki:
gyermekosztály TollEszköz of AbsztraktEszköz utolsó_egér_pozíció := invalid egér_gomb := fel függvény Mozgatás(pont) bemenet: A pont, ahova az egér elmozdul ha egér_gomb = le (rajzol egy vonalat az utolsó_egér_pozíció-tól a pont-ig) utolsó_egér_pozíció := pont függvény egérLe(pont) bemenet: A pont, ahol az egér van egér_gomb := le utolsó_egér_pozíció := pont függvény egérFel(pont) bemenet: A pont, ahol az egér van egér_gomb := fel
gyermekosztály KijelölőEszköz of AbsztraktEszköz kijelölés_kezdete := invalid egér_gomb := fel függvény Mozgatás(pont) bemenet: A pont, ahova az egér elmozdul ha egér_gomb = le (kijelöli a négyzetet a kijelölés_kezdete és a pont között) függvény egérLe(pont) bemenet: A pont, ahol az egér van egér_gomb := le kijelölés_kezdete := pont függvény egérFel(pont) bemenet: A pont, ahol az egér van egér_gomb := fel
Ebben a példában a context osztály a Kurzor
. Az absztrakt állapot osztályban (jelen esetben az AbsztraktEszköz
osztályban) megnevezett metódusok a Kurzor
osztályban is implementálva vannak. Ezek a metódusok a context osztályban meghívják az aktuális állapot megfelelő metódusait, amit az aktuálus_eszköz
képvisel.
osztály Kurzor aktuális_eszköz := new TollEszköz függvény Mozgatás(pont) bemenet: A pont, ahova az egér elmozdul aktuális_eszköz.Mozgatás(pont) függvény egérLe(pont) bemenet: A pont, ahol az egér van aktuális_eszköz.egérLe(pont) függvény egérFel(pont) bemenet: A pont, ahol az egér van aktuális_eszköz.egérFel(pont) függvény useTollEszköz() aktuális_eszköz := new TollEszköz függvény useKijelölőEszköz() aktuális_eszköz := new KijelölőEszköz
Vegyük észre, hogy a Kurzor
objektum ugyanúgy TollEszköz
-ként és KijelölőEszköz
-ként is képes viselkedni különböző helyzetekben úgy, hogy a megfelelő metódusokat átadja bármelyik épp aktív eszköznek. Ez az állapot minta lényege. Ebben az esetben megtehettük volna, hogy kombináljuk az állapotot és az eszközt, létrehozva a TollKurzor
és a KijelölőKurzor
osztályokat, ezzel leegyszerűsítve az egészet szimpla öröklésre, de gyakorlatban a Kurzor
olyan adatokat hordozhat, ami költséges és nem elegáns minden új eszközválasztásnál átmásolni egy új objektumba.
Java
[szerkesztés]Az állapot interfész két implementációval. Az állapot metódus hivatkozik a környezet objektumra, és képes állapotot változtatni.
interface Statelike {
void writeName(StateContext context, String name);
}
class StateLowerCase implements Statelike {
@Override
public void writeName(final StateContext context, final String name) {
System.out.println(name.toLowerCase());
context.setState(new StateMultipleUpperCase());
}
}
class StateMultipleUpperCase implements Statelike {
/** Counter local to this state */
private int count = 0;
@Override
public void writeName(final StateContext context, final String name) {
System.out.println(name.toUpperCase());
/* Change state after StateMultipleUpperCase's writeName() gets invoked twice */
if(++count > 1) {
context.setState(new StateLowerCase());
}
}
}
A környezet osztálynak van egy állapot objektuma, ami kezdetben alapértelmezett állapotot vesz fel, itt ez a StateLowerCase
. Metódusaiban az állapot objektum metódusait hívja.
class StateContext {
private Statelike myState;
StateContext() {
setState(new StateLowerCase());
}
/**
* Setter method for the state.
* Normally only called by classes implementing the State interface.
* @param newState the new state of this context
*/
void setState(final Statelike newState) {
myState = newState;
}
public void writeName(final String name) {
myState.writeName(this, name);
}
}
Az alábbi a használatot mutatja:
public class DemoOfClientState {
public static void main(String[] args) {
final StateContext sc = new StateContext();
sc.writeName("Monday");
sc.writeName("Tuesday");
sc.writeName("Wednesday");
sc.writeName("Thursday");
sc.writeName("Friday");
sc.writeName("Saturday");
sc.writeName("Sunday");
}
}
A fenti main()
main függvénnyel DemoOfClientState
kimenete a következő lehet:
monday TUESDAY WEDNESDAY thursday FRIDAY SATURDAY sunday
Jegyzetek
[szerkesztés]- ↑ a b Erich Gamma, Richard Helm, Ralph Johnson, John M. Vlissides. Programtervezési minták: Újrahasznosítható elemek tervezése objektumközpontú programokhoz. Kiskapu (1995). ISBN 963 9301 77 9
- ↑ a b Állapot minta UML-ben és LePUS3-ban. [2016. augusztus 5-i dátummal az eredetiből archiválva]. (Hozzáférés: 2015. február 15.)
- ↑ Jelmagyarázat. [2018. március 14-i dátummal az eredetiből archiválva]. (Hozzáférés: 2015. február 15.)
További információk
[szerkesztés]Fordítás
[szerkesztés]Ez a szócikk részben vagy egészben a State pattern című angol Wikipédia-szócikk ezen változatának fordításán alapul. Az eredeti cikk szerkesztőit annak laptörténete sorolja fel. Ez a jelzés csupán a megfogalmazás eredetét és a szerzői jogokat jelzi, nem szolgál a cikkben szereplő információk forrásmegjelöléseként.