Stav (návrhový vzor)

Návrhový vzor Stav je vhodným řešením v případě, kdy máme objekt, jež během své existence mění své vnitřní chování tak, že nabývá různých stavů. Přičemž chování objektu v různých stavech se výrazně liší. Při změně vnitřního stavu objektu se pak objekt reprezentující původní stav zamění za objekt jiný, jež odpovídá stavu novému.

Účel

Při programování může často dojít k situaci, kdy budeme mít objekt, jehož stav se může často měnit, přičemž změna tohoto stavu způsobí také změnu v chování objektu a tedy i v reakci na zasílané zprávy. V takovém případě si můžeme pro každý stav objektu definovat vnitřní objekt, jenž bude tento stav zastupovat. Tím se náš problém zjednoduší, neboť pohodlněji definujeme jednotlivé metody. Celý program pak bude přehlednější a přátelštější k případným dalším úpravám.

Výhody

  • odpadnou častá rozhodování
  • přehlednější program
  • snadnější upravování programu

Největší výhodou tohoto stavu je odpadnutí častých rozhodování. Odkaz na objekt původního stavu se jednoduše nahradí odkazem na objekt, který zastupuje stav nový. Atributy, které se nemění, jsou uloženy v původním objektu. Ty, které se však mění a charakterizují tak jednotlivé stavy, jsou uloženy ve vnitřním objektu.

Implementace

Pokud se tedy rozhodneme k využití návrhového vzoru stav, pak se náš problémový objekt - třída nabývající různých stavů rozdělí na další třídy:

  • třída, která představuje původní (vícestavový) objekt měnící svůj vnitřní stav
  • další jednostavové třídy, které zastupují jednotlivé vnitřní stavy objektu.

Díky této implementaci, kdy se každá jednostavová třída stará pouze o jeden určitý stav, dosáhneme jednodušší definice třídy, lepší přehlednosti a menší náchylnosti ke vzniku chyb. Budeme-li chtít v budoucnu přidat další stav, tak prakticky přidáme pouze novou jednostavovou třídu. Úpravy v ostatních jednostavových třídách pak budou minimalizovány nebo úplně eliminovány.

Postup při programování

Zde jsou uvedeny kroky, kterými je doporučeno se řídit, pokud budeme programovat mnohostavový objekt podle vzoru stav:

  • Rozmyslet si, které vlastnosti definovaných objektů závisí výrazně na stavu, v němž se objekt nachází.
  • Definovat stavové rozhraní, jehož instance budou představovat ony stavově závislé části oněch vícestavových objektů. V něm deklarovat metody odpovídající zprávám, na které reagují instance vícestavové třídy rozdílně v závislosti na svém stavu.
  • Pro každý stav definovat speciální „jednostavovou“ třídu, která bude implementovat stavové rozhraní a definovat chování svých instancí ve stavu, jenž bude instance této třídy zastupovat.
  • V definici třídy vícestavového objektu deklarovat atribut odkazující na instanci stavového rozhraní a následně jej inicializovat odkazem na objekt, jehož typ odpovídá výchozímu stavu vytvářené instance.

Rozhraní x abstraktní třída

Pokud vás napadá možnost použít místo rozhraní (rozhraní ve smyslu interface) abstraktní třídu, pak jste objevili druhou správnou možnost. Vícestavová třída stejně komunikuje se svými stavovými objekty přes deklarované rozhraní, a proto záleží už jen na vás či na konkrétní implementaci, jakou možnost zvolíte. I v případě, kdy budou mít dva různé stavy některou metodu společnou, je výhodnější, definovat ji jako abstraktní, aby se zamezilo vzniku vazeb mezi třídami. Tyto vazby by se v budoucnu mohly ukázat jako nežádoucí, a tak bychom pouze dosáhli negativního efektu.

Přepínání stavu

Jistě jste si všimli, že dosud nebylo uvedeno, jak ono přepínání mezi stavy objektu vlastně funguje. Ve skutečnosti vše záleží na konkrétní aplikaci. Někdy má změnu stavu na starosti instance vícestavové třídy. Jindy je tato odpovědnost přenechána instancím jednostavových tříd, které vracejí odkaz na instanci zastupující stav nový.

Instance vícestavového objektu jako parametr

V případě, kdy nějaká stavová třída potřebuje častěji komunikovat s atributy či metodami vícestavového objektu, mohlo by se jevit jako vhodné řešení, definovat tuto třídu jako vnitřní. Avšak tento postup se rozhodně nedoporučuje, jelikož by se vše spíše ztížilo. Vhodnější bude, když jednostavové instanci předáme instanci vícestavového objektu jako parametr.

Příklad

(celý text vychází z knihy p. Pecinovského - Návrhové vzory)

Představme si grafický objekt - autíčko, které může zatáčet o 90° a jezdit ve 4 směrech. Při každém zatočení se musí autíčko chovat jinak a také musí změnit svůj vzhled tak, aby odpovídal směru jízdy. Balíček tříd s řešením takového autíčka, které je reprezentováno jakýmsi obrázkem a které umí zatáčet, čili také měnit směr jízdy (svůj stav), by mohl mít dvě verze:

  • balíček, v němž jednostavové třídy implementují rozhraní IAuto1:

Stav - interface.png

  • balíček, kde jsou jednostavové třídy potomky abstraktní třídy AAuto1:

Stav - abstractClass.png

Obě řešení jsou velice podobná, třídy SAuto, VAuto, JAuto a ZAuto představují jednotlivé stavy, jsou to tedy ony jednostavové třídy, přičemž každá z nich řeší vzhled a chování autíčka jedoucího na sever, jih, východ či západ. Instance třídy Auto4 budou již konkrétní autíčka, která mohou např. jezdit po nějakém okruhu a závodit. Rozhraní IAuto1 deklaruje požadavky na jednostavové třídy a atribut třídy Auto4 se na něj odkazuje. V možnosti s abstraktní třídou je tato společným rodičem jednostavových tříd a obsahuje společné implementace. Zdrojové kódy řešící přesně tento příklad zde neuvádím, jelikož by byly rozsáhlé a navíc jsou k nalezení na stránce doprovodných programů ke knize : Návrhové vzory.

Související vzory

Zde jsou uvedeny návrhové vzory, které souvisí se vzorem stav a mohly by být užitečné při řešení vašeho problému:

  • Jedináček (Singleton) - stavové objekty často implementují právě tento vzor
  • Interpret (Interpreter) - může využít stav k definování parsovacího kontextu
  • Muší váha (Flyweight) - vysvětluje sdílení stavových objektů
  • Strategie (Strategy) - podobný vzoru stav, odlišuje se účelem. (2)

Literatura

Externí odkazy

Média použitá na této stránce

Stav - interface.png
Diagram použití návrhového vzoru stav s interface
Stav - abstractClass.png
Diagram použití návrhového vzoru stav s abstraktní třídou