Á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ó 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 szerkesztés

 
Állapot UML-ben[1][2]
 
Állapot LePUS3-ban[2][3]

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

  1. 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 
  2. 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.)
  3. 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.