Állami tervezési minta Java-ban

1. Áttekintés

Ebben az oktatóanyagban bemutatjuk az egyik viselkedési GoF tervezési mintát - az állam mintát.

Először áttekintést adunk a céljáról és elmagyarázzuk a problémát, amelyet megpróbál megoldani. Ezután megnézzük az állam UML-diagramját és a gyakorlati példa megvalósítását.

2. Állami tervezési minta

Az állami minta fő gondolata az, hogy engedje meg, hogy az objektum megváltoztassa viselkedését anélkül, hogy megváltoztatná az osztályát. Továbbá a kód megvalósításával tisztábbnak kell maradnia sok if / else utasítás nélkül.

Képzelje el, hogy van egy csomagunk, amelyet postára küldünk, maga a csomag megrendelhető, majd postára szállítható, és végül az ügyfél megkapja. Most, a tényleges állapottól függően, ki akarjuk nyomtatni a szállítási állapotát.

A legegyszerűbb megközelítés az, ha néhány logikai zászlót adunk hozzá, és egyszerű if / else utasításokat alkalmazunk az osztály minden módszerén belül. Ez egy egyszerű forgatókönyv esetén nem sokat bonyolít. Ez azonban bonyolíthatja és szennyezheti a kódunkat, amikor több állapotot fogunk feldolgozni, ami még több if / else utasítást eredményez.

Ezenkívül az egyes állapotok logikája minden módszerre kiterjedne. Most itt lehet az állam mintáját használni. Az állam tervezési mintájának köszönhetően a logikát dedikált osztályokba foglalhatjuk, alkalmazhatjuk az Egységes Felelősség Elvét és a Nyitott / Zárt Elvet, tisztább és karbantarthatóbb kóddal rendelkezhetünk.

3. UML diagram

Az UML diagramban ezt látjuk Kontextus osztály társított Állapot amely a program futtatása során megváltozik.

Kontextusunk megy delegálja a viselkedést az állam végrehajtására. Más szavakkal, az összes beérkező kérést az állam konkrét végrehajtása fogja kezelni.

Látjuk, hogy a logika el van választva, és az új állapotok hozzáadása egyszerű - egy másik hozzáadásával jár Állapot szükség esetén a megvalósítás.

4. Végrehajtás

Tervezzük meg az alkalmazásunkat. Mint már említettük, a csomag megrendelhető, szállítható és átvehető, ezért három állapotunk és a kontextus osztályunk lesz.

Először határozzuk meg a kontextust, ez a Csomag osztály:

public class Package {private PackageState state = new OrderedState (); // getter, setter public void previousState () {state.prev (this); } public void nextState () {state.next (this); } public void printStatus () {state.printStatus (); }}

Mint láthatjuk, tartalmaz egy hivatkozást az állam kezelésére, értesítést előzőState (), nextState () és printStatus () módszerek, ahol a feladatot az állami objektumra delegáljuk. Az államok összekapcsolódnak majd egymással és minden állam másikat fog alapozni ez referencia átadták mindkét módszernek.

Az ügyfél kölcsönhatásba lép a Csomag osztályban, mégsem kell az állapotok beállításával foglalkoznia, az ügyfélnek csak annyit kell tennie, hogy a következő vagy az előző állapotba megy.

Ezután megkapjuk a PackageState amelynek három módszere van a következő aláírásokkal:

nyilvános felület PackageState {void next (Package pkg); void prev (Csomag pkg); void printStatus (); }

Ezt a felületet minden konkrét állapotosztály megvalósítja.

Az első konkrét állapot az lesz Rendelt állam:

public class OrderedState implementálja a PackageState {@Override public void next (Package pkg) {pkg.setState (new DeliveredState ()); } @Orride public void prev (Package pkg) {System.out.println ("A csomag gyökér állapotban van."); } @Orride public void printStatus () {System.out.println ("A csomag megrendelve, még nem szállítottuk az irodába."); }}

Itt mutatunk a következő állapotra, amely a csomag megrendelése után következik be. A rendezett állapot a gyökérállapotunk, és ezt kifejezetten megjelöljük. Mindkét módszerben láthatjuk, hogyan kezelik az állapotok közötti átmenetet.

Vessünk egy pillantást a DeliveredState osztály:

public class DeliveredState implementálja a PackageState {@Override public void next (Package pkg) {pkg.setState (new ReceivedState ()); } @Orride public void prev (Package pkg) {pkg.setState (new OrderedState ()); } @Orride public void printStatus () {System.out.println ("A csomagot postára szállították, még nem érkezett meg."); }}

Ismét összefüggést látunk az államok között. A csomag megváltoztatja állapotát rendeltről kézbesítettre, az üzenet a printStatus () változások is.

Az utolsó állapot: ReceivedState:

public class ReceivedState implementálja a PackageState {@Override public void next (Package pkg) {System.out.println ("Ezt a csomagot már megkapta az ügyfél."); } @Orride public void prev (Package pkg) {pkg.setState (new DeliveredState ()); }}

Itt érjük el az utolsó állapotot, csak visszagörgethetünk az előző állapotba.

Már látjuk, hogy van némi kifizetés, mivel az egyik állam tud a másikról. Szorosan összekapcsoljuk őket.

5. Tesztelés

Lássuk, hogyan viselkedik a megvalósítás. Először ellenőrizzük, hogy a telepítési átmenetek a várt módon működnek-e:

@Test public void givenNewPackage_whenPackageReceived_thenStateReceived () {Package pkg = new Package (); assertThat (pkg.getState (), instanceOf (OrderedState.class)); pkg.nextState (); assertThat (pkg.getState (), instanceOf (DeliveredState.class)); pkg.nextState (); assertThat (pkg.getState (), instanceOf (ReceivedState.class)); }

Ezután gyorsan ellenőrizze, hogy csomagunk vissza tud-e lépni az állapotával:

@Test public void givenDeliveredPackage_whenPrevState_thenStateOrdered () {Package pkg = new Package (); pkg.setState (új DeliveredState ()); pkg.previousState (); assertThat (pkg.getState (), instanceOf (OrderedState.class)); }

Ezt követően ellenőrizzük az állapot megváltoztatását, és nézzük meg, hogyan printStatus () A módszer futás közben megváltoztatja végrehajtását:

public class StateDemo {public static void main (String [] args) {Package pkg = new Package (); pkg.printStatus (); pkg.nextState (); pkg.printStatus (); pkg.nextState (); pkg.printStatus (); pkg.nextState (); pkg.printStatus (); }}

Ezzel a következő kimenetet kapjuk:

A csomagot megrendelték, még nem szállították az irodába. A csomagot postára szállították, még nem érkezett meg. A csomagot az ügyfél fogadta. Ezt a csomagot már megkapta az ügyfél. A csomagot az ügyfél fogadta.

Amint megváltoztattuk a kontextus állapotát, a viselkedés változott, de az osztály változatlan. Az általunk használt API mellett.

Az állapotok közötti átmenet is megtörtént, osztályunk megváltoztatta állapotát és következésképpen viselkedését.

6. Hátrányok

Az állapotmintázat hátránya az államok közötti átmenet megvalósításának kifizetése. Ez keményen kódolja az államot, ami általában rossz gyakorlat.

De szükségleteinktől és követelményeinktől függően ez lehet vagy nem kérdés.

7. Állam vs stratégia minta

Mindkét tervezési minta nagyon hasonló, de az UML diagramjuk ugyanaz, a mögöttük levő ötlet kissé eltér.

Először is a stratégiai minta meghatározza a felcserélhető algoritmusok családját. Általában ugyanazt a célt érik el, de más megvalósítással, például algoritmusok rendezésével vagy megjelenítésével.

Állapotmintában a viselkedés teljesen megváltozhat, a tényleges állapot alapján.

Következő, a stratégiában az ügyfélnek tisztában kell lennie a lehetséges stratégiákkal, amelyek kifejezetten felhasználhatók és megváltoztathatók. Míg állapotmintában az egyes állapotok kapcsolódnak egy másikhoz, és létrehozzák az áramlást, mint a Véges állapotgépben.

8. Következtetés

Az állami tervezési minta nagyszerű, amikor szeretnénk kerülje a primitív if / else állításokat. Ehelyett mi különítsük el a logikát külön osztályokba és hagyjuk kontextus objektum delegálja a viselkedést az állami osztályban megvalósított módszerekhez. Emellett kihasználhatjuk az állapotok közötti átmeneteket, ahol az egyik állapot megváltoztathatja a kontextus állapotát.

Általában ez a tervezési minta viszonylag egyszerű alkalmazásokhoz kiváló, de a fejlettebb megközelítés érdekében bepillanthatunk a Spring's State Machine oktatóanyagába.

Szokás szerint a teljes kód elérhető a GitHub projekten.


$config[zx-auto] not found$config[zx-overlay] not found