Á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.

Á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ó objektum-orientá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

Programtervezési minták
Java-kódpéldák
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áékprogramtervezé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ó objektum-orientá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 = @ 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 > { 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 Programtervezési minták Java-kódpéldák