Az állapot programtervezési minta nagyon hasonlít a stratégia mintára. Ez egy viselkedésiprogramtervezé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
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.
interfaceStatelike{voidwriteName(StateContextcontext,Stringname);}classStateLowerCaseimplementsStatelike{@OverridepublicvoidwriteName(finalStateContextcontext,finalStringname){System.out.println(name.toLowerCase());context.setState(newStateMultipleUpperCase());}}classStateMultipleUpperCaseimplementsStatelike{/** Counter local to this state */privateintcount=0;@OverridepublicvoidwriteName(finalStateContextcontext,finalStringname){System.out.println(name.toUpperCase());/* Change state after StateMultipleUpperCase's writeName() gets invoked twice */if(++count>1){context.setState(newStateLowerCase());}}}
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.
classStateContext{privateStatelikemyState;StateContext(){setState(newStateLowerCase());}/** * Setter method for the state. * Normally only called by classes implementing the State interface. * @param newState the new state of this context */voidsetState(finalStatelikenewState){myState=newState;}publicvoidwriteName(finalStringname){myState.writeName(this,name);}}
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.