Ilja Kraval : Ukázka a příklad vzoru TEMPLATE METHOD (4.2.2003)

Návrhové vzory se většinou používají ve svých kombinacích. Při praktickém použití vzorů většinou dochází k využití hned několika vzorů najednou a nikoliv pouze jednoho. To je třeba mít při jejich studiu a poté praktickém použití na paměti.

Věnujme se opět problematice přepínače - switche. Jak bylo ukázáno v "Článku 1" této sekce, je výhodnější zavést switch nikoliv "natvrdo" pomocí klasické konstrukce přepínače, ale nějakým jiným způsobem a pokud možno co nejflexibilněji. Při flexibilním řešení lze přidávat další větve přepínače, aniž by se muselo zasahovat do již existujícího kódu

Jedním z řešení je použít vzor COMMAND. V tom případě je switch reprezentován kolekcí (seznamem) z objektů ze tříd COMMAND1, COMMAND2 atd., které polymorfně přepisují operaci Execute(). Kolekce se naplní v run-time postupným voláním operací Add(a_COMMAND, a_key). Vyvolání odpovídající větve přepínače se provede tak, že nejprve kolekce podle klíče vrátí odpovídající objekt a u tohoto objektu se zavolá operace Execute(). Větev přepínače je takto reprezentována konkrétní implementací operace Execute()

Druhý možný způsob uvádí také "Článek 1" a tomuto způsobu se budeme věnovat blíže z toho důvodu, že je poučný jako ukázka kombinace vzorů.

Jako výchozí použijeme vzor CHAIN OF RESPONSIBILITY. Myšlenka tohoto vzoru spočívá v zavedení zřetězeného seznamu objektů, které sice pocházejí z různých tříd, ale mají díky společnému předkovi stejný interface a tedy kompatibilní přepisující metodu HandleRequest(). Díky tomuto společnému interfacu jsou všechny objekty z tohoto zřetězeného seznamu kompatibilní a řetěz lze poskládat v run-time. Dostáváme tak podobnou konstrukci jako u předešlého řešení se vzorem COMMAND s tím rozdílem, že klíč se stává vstupním parametrem operace HandleRequest(a_key). Uvnitř této operace se zjištuje, zda klíč patří ke zpracování. Pokud hodnota odpovídá zpracování, potom se volá operace pro zpracování. Pokud tato hodnota klíče nepatří ke zpracování, volá se operace HandleRequest(a_key) následníka v řetězu. Rozdíl mezi řešeními je v tom, že při použití vzoru COMMAND se opíráme o funkcionalitu klíčované kolekce, která umí vrátit objekt podle klíče. V řetězu rocházíme item po itemu až po ten, kterému hodnota patří (resp. ošetřujeme výjimku pádu na konci řetězu).

Použití CHAIN OF RESPONSIBILITY v tomto případě je však tak trochu modifikované. Původně ve vzoru je totiž metoda HandleRequest(a_key) svými potomky přepisována. Nyní však přepisována není a je zavedena jako metoda TEMPLATE METHOD dávající scénář zapsaný v JAVACIS podle tohoto schématu

    class RequestSwitchHandler;
    ...
    public void abstract Process();   
    public void HandleSwitchRequest(integer a_key);
     {
      if mKey = a_Key 
         then Process();
         else mNextHandler.HandleSwitchRequest(a_Key);
     }       
           
   

Každý potomek třídy RequestSwitchHandler nepřepisuje samotnou metodu HandleSwitchRequest, ale metodu Process, která se volá až po rozhodnutí "hodnota je moje".

Jedná se o klasickou ukázku vzoru TEMPLATE METHOD. Metoda HandleSwitchRequest je realizací tohoto vzoru. Metoda zavádí scénář volání metod, které jsou "zespodu" přepsány. Sama metoda TEMPLATE METHOD se nepřepisuje. Všimněme si, že takto došlo nejenom ke kombinaci dvou vzorů (CHAIN OF RESPONSIBILITY a TEMPLATE METHOD), ale i k mírné modifikaci při použití vzoru CHAIN OF RESPONSIBILITY. V aplikování tohoto vzoru zde v tomto příkladu se nepoužívá přepisovaná metoda vzoru, jak předepisuje vzor, ale nepřepisovaná metoda TEMPLATE METHOD.

Drobná poznámka: V kódu uvedeném nahoře nejsou uvedeny ještě dvě důležité metody: Metoda pro dosazení následníka řetězu (SetNextHandler), která plní objektovou referenci na mNextHandler a metoda pro plnění hodnoty mKey (SetKey). Tyto metody se volají při inicializaci řetězu.


V případě jakýchkoliv připomínek napište prosím na adresu objects@objects.cz