Flyweight
Flyweight (česky muší váha) je jedním ze základních návrhových vzorů programů. Muší váha patří do skupiny strukturních vzorů, které umožňují řízení počtu instancí objektů. Vzor je vhodný pro situace, kdy vzniká mnoho malých objektů, u kterých je možné podstatnou část jejich stavu sdílet nebo ho nahradit výpočtem.
Motivace
Vzor muší váha je vhodný pro situace s mnoha objekty. Klasickým příkladem je reprezentace jednoho znaku v textovém editoru [1],[2].
Znak (Glyph
) je určen svou polohou v dokumentu, použitým fontem, barvou, jeho velikostí a v neposlední řadě také vlastním kódem znaku (např. 'a'). Pokud popis Znaku (Glyph
) obsahuje všechny uvedené informace, tak dochází k velmi výraznému růstu paměťové náročnosti s růstem velikosti dokumentu, neboť popis jednotlivého znaku může zabírat i stovky bytů. Muší váha poskytuje řešení tohoto stavu. Zavádí možnost sdílet jednu instanci znaku pro všechny jeho shodné výskyty.
Popis vzoru
Vzor je založen na vyčlenění vnějšího stavu z objektu. Tímto vnějším stavem se rozumí takové atributy, které je možno uchovávat vně objektu nebo je vypočítat. Tento vnější stav je často označován jako kontext objektu. Metody, které pro svou funkci potřebují hodnoty, které jsou součástí vnějšího stavu, je dostávají až v okamžiku svého volání jako parametr - kontext. Zredukovaný objekt (bez vnějšího stavu) je možné obvykle sdílet mezi jednotlivými užitími. Tento objekt již pouze obsahuje atributy, které jsou označovány jako vnitřní stav. Pro možnost sdílení těchto objektů je zapotřebí továrna, která zajistí jejich tvorbu (pokud doposud vhodný objekt neexistuje) nebo vrácení existujícího. Muší váha přináší úsporu paměťového prostoru, a to dvojím způsobem: redukcí velikosti jednotlivých objektů (nahrazením vnějšího stavu objektu výpočtem nebo efektivnější reprezentací) a sdílením jednotlivých instancí objektů reprezentujících vnitřní stav.
Příklad textového editoru
U textového editoru z motivačního příkladu se ukazuje, že mnoho znaků používá font se shodnými vlastnostmi, tj. v typickém dokumentu je použito obvykle do deseti druhů písma. Informace o vlastnostech použitého fontu (metrika) tedy nemusí být součástí každého písmene, ale je vhodné ji vyčlenit do sdílených vlastností.
Obdobný postup je možno aplikovat i na znak samotný, neboť i jednotlivých typů znaků je v dokumentu obvykle použito výrazně méně, než je počet jejich výskytů. Lze tedy definovat třídu Glyph
, která popisuje použití kombinace znaku a fontu. Text je pak reprezentován vhodnou skladbou odkazů na Glyph
objekty. Jednotlivé objekty je však možno sdílet mezi jednotlivými použitími. Pro zajištění správného sdílení je nutné jejich vytváření pomocí továrny – GlyphFactory
. Továrna zajistí vytvoření nového objektu, jen pokud doposud neexistuje; v opačném případě vrací existující objekt.
Následující diagram ukazuje reprezentaci textu „BABETA“ s využitím muší váhy. Jednotlivá písmena jsou vytvářena pomocí továrny a odstavec jen udržuje odkazy na ně.
Pokud je zapotřebí vykreslit blok textu, tak pro jednotlivé objekty (Glyph
) je volána metoda draw(Context context)
, která jako vstupní parametr dostává kontext. Tímto kontextem je poloha znaku v rámci odstavce. Tato poloha nemusí být přímo uložena jako součást znaku, neboť ji je možno odvodit/vypočítat z jeho předchůdců v rámci odstavce.
Vhodnost použití
Návrhový vzor slouží pro snížení počtu instancí objektů a paměťové náročnosti. Vzor je vhodné použít v těchto případech (měly by být naplněny všechny podmínky):
- značné množství objektů
- objekty jsou náročné na úložný prostor
- podstatné množství informací o objektu lze převést do vnějšího stavu nebo je vypočítat
- zbývající vnitřní stavy objektu již vytvářejí objekty, které lze snadno sdílet
- u sdílených vnitřních stavů nezáleží na identitě objektu a sdílení je efektivní
Schéma
Schéma znázorňuje použití objektu muší váhy (Flyweight
) klientem. Jednotlivé sdílené muší váhy jsou vytvářeny pomocí továrny (FlyweightFactory
).
Sdílení muších vah
Důležitou součástí návrhového vzoru muší váhy je možnost sdílení muších vah jednotlivými klienty. Pokud není možné tento předpoklad splnit pro všechny takovéto objekty, je možné použít nepatrně modifikované schéma, které tuto situaci řeší.
Objekt UnsharedFlyweight
je objekt, který není sdílený, zatímco objekty SharedFlyweight
jsou sdíleny a jako takové jsou vytvářeny továrnou FlyweightFactory
.
Historie
Návrhový vzor byl poprvé popsán v roce 1990 v článku Glyphs: Flyweight Objects for User Interfaces [1] a následně zařazen do přehledu vzorů [2].
Příbuzné vzory
Muší váha pro svou realizaci využívá návrhový vzor továrna pro vytváření jednotlivých sdílených objektů. Odkazy na sdílené objekty jsou často uspořádány ve složitější struktuře pomocí vzoru skladba. Vzor muší váha může být použit pro implementaci vzorů stav a strategie.
Vytváření muší váhy při refaktorizaci
Návrhový vzor je možné použít již ve fázi návrhu nebo s jeho pomocí refaktorovat stávající kód. Při refaktorizaci je možno využít následujícího postupu:
- Zvážit výhody/náklady použití vzoru
- Identifikace budoucího vnějšího a vnitřního stavu objektů
- Zapouzdření vnějšího stavu do samostatné třídy Context (budoucí kontext volání), nahrazení původních atributů třídy, které ho popisují novým objektem context typu Context.
- Vyjmutí vnějšího stavu (context) z objektu a přidání jako parametr do metod, které ho používají.
- Nahrazení konstrukce třídy tovární metodou, parametrem bude vnitřní stav
- Nahrazení tovární metody samostatnou továrnou
Při refaktorizaci tímto postupem lze po každý z kroků 3-6 vytvořit a spustit příslušné testy při zachování relativně malých změn.
Diskuse
V mnoha praktických použitích se může ukázat výhodné použít jen část vzoru muší váha.
Použití pouze vyčlenění vnějšího stavu
Při refaktorizaci lze využít pouze vyčlenění vnějšího stavu mimo objekt. Tímto krokem může dojít ke zmenšení složitosti objektu. Přístup je vhodný, pokud lze hodnoty uložit/vypočítat vně objektu a jsou potřebné jen v malém množství metod. Současně je však zapotřebí zachovat identitu jednotlivých objektů. Při vhodném použití dojde ke snížení paměťové náročnosti objektů, zvýšení jejich flexibility, neboť se již dále nestarají o svůj vnější stav. Nevýhoda tohoto postupu může spočívat při nedokonalé refaktorizaci ve zvýšení složitosti třídy uchovávající/vypočítávající vnější stav.
Použití pouze sdílení objektů
V případě, kdy lze objekty snadno sdílet a neobsahují žádný vnější stav, tak stačí pouze zavést tovární třídu. Následně je již přímá cesta ke sdílení objektů. V případě, kdy jsou takto sdíleny objekty, však již nemohou obsahovat přímou vazbu na své nesdílené rodiče.
Literatura
- Paul R. Calder; Mark A. Linton. Glyphs: Flyweight Objects for User Interfaces. In: ACM User Interface Software Technology Conference. [s.l.]: ACM, 1990. S. 92–101. (Anglicky)
- GAMMA, Erich; HELM, Richard; JOHNSON, Ralph; VLISSIDES, John. Design Patterns: Elements of Reusable Object-Oriented Software. [s.l.]: [s.n.], 1995.
- FOWLER, Martin. Refactoring – Improving the Design of Existing Code. [s.l.]: [s.n.], 2002.