Návrhové vzory (design patterns) - diplomová práce M. Dvořáka


Decorator Pattern

SKUPINA: Structural Patterns

Problém

Decorator je řešením pro problém, jak změnit vlastnosti instance třídy bez nutnosti vytvářet novou odvozenou třídu. Představuje možnost jak dynamicky složit chování objektů.

Podmínky

Představme si, že v programu existuje dvacet tlačítek a my chceme, aby se pět z nich chovalo jiným způsobem respektive měli rozšířené funkčnosti. Můžeme zvolit metodu vytvoření nových tříd odvozených z třídy tlačítko. Ve většině případů to bude i vhodné řešení. Ovšem v situaci, kdy pro každé tlačítko chceme nadefinovat trochu odlišnou funkčnost (například okraj tlačítka), dostáváme v našem příkladě nových pět tříd. Tento postup může učinit výsledný kód nepřehledný.

Další možností využití decoratoru je situace, kdy potřebujeme dynamicky přidávat nové funkčnosti jednotlivým objektům.

Řešení

Component představuje abstraktní rozhraní pro objekty, kterým budou přidávány nové funkčnosti. Například to může být obecné rozhraní pro všechny vizuální komponenty. Concrete Component je objekt, jehož chování je doplněno (např. tlačítko) a implementuje obecné rozhraní. Decorator obsahuje reference na komponentu (tj. zapouzdřuje komponentu) a implementuje komponentové rozhraním. Všechny operace, které jsou vyvolány na třídě Decorator, jsou přesměrovány na odpovídající metody zapouzdřeného objektu Concrete Component. Navíc je možné přidat dodatečné operace před a za přesměrování volání. Concrete Decorator A rozšiřuje komponentu přidáním atributu, Concrete Decorator B je rozšíření o další metodu. Metoda operation() v Concrete Decorator typu B je složena z volání metody operation() třídy Decorator, zajišťující volání metody operation v Concrete Component, a metody added behavior().

Příklad

Předpokládejme, že každé vypěstované ovoce nebo zeleninu je nutné dopravit do centrálního skladu, aby bylo dál možné je využít v dodavatelském řetězci. Velkoobchod má možnost zjišťovat, jaké množství zboží je možné dodat do skladu. Předpokládejme, že ovoce a zelenina se nedováží, ale získává od pěstitele. Sklad má referenci na tohoto pěstitele a získává od něho informace o plánované úrodě. Tyto data ovšem ještě upravuje na základě svých kapacitních a technologických možností. Velkoobchod tedy obdrží již odfiltrovaná data. Filtr sklad tedy rozšířil funkčnost Pěstitele, na kterého udržuje referenci.

Výsledek

Bez nutnosti vytvoření nových podtříd, byla rozšířena funkčnost výchozí třídy respektive konkrétních instancí této třídy. Rozhraní, které je využívané klientem, zůstává po vytvoření Decoratoru stejné, protože Decorator i Concrete Component implementují stejné rozhraní. Decorator získává referenci na komponentu například v parametru konstruktoru.

Odůvodnění a souvislosti

Decorator představuje více flexibilní možnost rozšíření funkčnosti, než je použití dědění z původní třídy, protože rozšiřuje funkčnosti konkrétních instancí těchto tříd.

Může nastat problém s určením třídy, protože Decorator a Concrete Component nejsou shodné, a proto nelze úspěšně testovat typ objektu. Decorator dále může vést k systému s mnoha malými objekty.

Decorator můžeme použít pro dynamické skládání chování. Například může být vyžadována definice mnoha aritmetických funkcí, s kterými bude v programu pracováno. S využitím Decoratoru není nutné pro každou funkci vytvářet zvláštní třídu. Je možné definovat hierarchii základních aritmetických funkcí (cos, sin atd.), které vycházejí z abstraktní třídy Function. Každá třída odvozená z Function udržuje referenci na jinou funkci, jež pro ni představuje zdroj dat. Základní funkce mají jako zdroj dat přímo čísla. Další funkce vlastní referenci na základní a využívají jejich výsledky, s kterými dále pracuje. Takto můžeme dynamicky vytvářet objekty představující složité funkce, bez nutnosti definovat speciální třídy viz MET02.

Související vzory

  • Strategy - Decorator je dobré použít, jestliže chceme zajistit nějakou změnu před nebo po voláním operace jiného objektu. Jestliže chceme měnit věci, které se stanou uprostřed volání metody, je vhodné zvažovat využití Strategy.
  • Template Method - jedná se o alternativní vzor k Decoratoru, ale umožňuje i modifikovat algoritmus metody změnou jeho kroků.
  • Composite - představuje podobný vzor svojí strukturou, oba dva implementují obdobný způsob organizace s využítím rekurze. Rozdílný je účel použití, kdy Decorator se nesnaží o uspořádání, ale o změnu chování objektu.

Reference

  • java.io package - princip rozšiřování čtení ze souboru, diagramy převzaty z www.javaworld.compovolené sítě.BufferedReader and FilterReader představují třídy typu Decorator. Jsou odvozeny z abstraktní třídy Reader a mají referenci na jinou instanci třídy Reader, kterou upravují. Na tuto instanci jsou přesměrovány volání metod, které mohou rozšířit.
    Příklad vytvoření LineNumberReader:
LineNumberReader aLineReader = new LineNumberReader(new FileReader(NazevSouboru);



  • java.io.package - třídy pracující ze sériovým uspořádáním bytů nebo znaků - stream třídy, bližší informace v MET02.

Stránku naposledy upravoval Lubos Pavlicek, 16.06.2005, 19:04