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.
Áttekinté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
Példa
Pszeudokód 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 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
További információk Állapot programtervezési minta Állapot programtervezési minta - példakód C++-ban
Fordítás
Kategória:Programtervezési minták Kategória:Java-kódpéldák