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:
- balíček, kde jsou jednostavové třídy potomky abstraktní třídy AAuto1:
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:
Literatura
- Pecinovský, Rudolf. 2007. Návrhové vzory. 1. vyd. Brno: Computer Press, a.s., 2007. ISBN 978-80-251-1582-4.
- Pavlíček, Luboš. [Online] 16. 6 2005. [Citace: 5. 12 2009.] https://web.archive.org/web/20090304211925/http://objekty.vse.cz/Objekty/Vzory-State.