150 likes | 319 Views
Composite [kompozit, ne kompozajt]. Composite. V ý slovnost kompoz i t, ne kompozajt Účel Popisuje, jak postavit hierarchii tříd složenou ze dvou druhů objektů Atomických (primitivních) Složených (rekurzivně složených z primitivních a dalších složených objektů)
E N D
Composite • Výslovnost • kompozit, ne kompozajt • Účel • Popisuje, jak postavit hierarchii tříd složenou ze dvou druhů objektů • Atomických (primitivních) • Složených (rekurzivně složených z primitivních a dalších složených objektů) • Jednotný přístup k atomickým i složeným objektům (kontejnery) • Motivace • Grafický editor • Vytváření složitých schémat z primitivních komponent • Použití těchto schémat na vytvoření ještě složitějších schémat • Hloupá implementace – bez Composite • Definuje třídy pro grafická primitiva (Line, Text, Rect) a třídy jako jejich kontejnery • Kód používající tyto třídy musí rozlišovat primitiva a kontejnery • Lepší implementace - Composite pattern • Klient nemusí rozlišovat
Příklad: Model tříd Component • Struktura: • Abstraktní třída Graphic (Component) • Reprezentuje primitivní třídy i kontejner • Deklaruje metodu Draw() • Deklarujemetody pro správu potomků (případně poskytuje defaultní definice) • Primitivní podtřídy (Leaf) přímo vykonávají své metody Draw() • Kontejnery (Composite) definují Draw() a metody pro správu potomků • Delegují Draw() na všechny své potomky Composite Leaves
Component (Graphic) Deklaruje interface pro objekty v kompozici Implementuje defaultní chování společného interface Deklaruje interface pro správu a přístup k potomkům Uchovává referenci na předka (volitelně) Leaf(Rectangle, Line, Text, atd.) Reprezentuje listové objekty v kompozici, nemá potomky Definuje chování primitivních objektů Composite(Picture) Definuje chování komponent, které potomky mají Uchovává potomky ve vhodné datové struktuře a umožňuje jejich správu Client Používá objekty v kompozici přes interface Component Composite – účastníci
Důsledky • Definuje hierarchii tříd skládajících se z primitivních a složených objektů, rekurzivně • Kdykoliv klientský kód předpokládá primitivní objekt, může také použít složený objekt • Zjednodušuje klienta • Může zacházet stejně se složenými i primitivními objekty • Nemusí je rozlišovat – jednodušší kód • Umožňuje jednoduché přidávání nových komponent • Nově definované Composite nebo Leaf třídy automaticky fungujís existujícími strukturami i klientským kódem • Na klientovi se nemusí nic měnit • Může Váš design učinit až příliš obecným • Jednoduché přidání nových komponent naopak omezuje možnosti,jak omezit typy komponent ve složeném objektu • Je potřeba použít run-time kontroly
Implementace – „a co děti?“ • Deklarace operací s potomky (add, remove, getChild) • Operace pro správu dětí nemají smysl pro listy – kam s nimi? • V root Component – přináší transparentnost, méně bezpečné • Preferovaná varianta podle GoF • „Divné“ operace v listech, řešit výjimkami • V Composite – přináší bezpečnost, odnáší transparentnost • Chyby (snaha přidat potomek k listu) zachyceny už při kompilaci • Leaf a composite mají jiný interface • Ve třídě Component definovat virtuální metodu GetComposite() vracející defaultně null • Datová struktura pro uchování potomků • Pole, spojové seznamy, stromy, hashovací tabulky, … • Nedefinovat kolekci ve třídě Component ! • Pořadí potomků • Composite může definovat pořadí potomků, které se může v aplikaci využít • Příklad v Graphics: front-to-back order • Použít vhodnou datovou strukturu • Přizpůsobit interface metod pro přístup a správu potomků, lze použít Iterator pattern
Implementace • Explicitní reference na rodiče • Jednodušší pohyb po stromové struktuře, použití ve vzoru Chain of Responsibility • Referenci definovat v Component • Leaf a Composite ji dědí spolu s funkcemi s ní případně spojenými • Zajistit, aby reference byla aktuální • Měnit ji pouze když je dítě přidáváno nebo odstraňováno z Composite (Add, Remove) • Mazání komponent • Mazaný Composite by měl být zodpovědný za smazání svých dětí • Výjimka: sdílené komponenty • Při mazání dítěte je potřeba jej odebrat z rodičovy kolekce • Composite.Operation() • Nemusí se delegovat pouze na potomky • Př.: Composite.Price() vrací cenu objektu:rekurzivně zavolá na všechny své děti a jejich návratové hodnoty sečte
Implementace - vylepšení • Cachování pro zlepšení výkonnosti • Při častém procházení nebo prohledávání velkých kompozic je dobré evidovat informace z posledního průchodu / hledání • Composite cachuje informace o dětech • Př.: Picture cachuje ohraničenou viditelnouoblast svých dětí,během překreslování je nemusí procházet znovu • Je třeba invalidovat cache při změně • Jsou potřeba reference na předky a interface k invalidaci cache • Sdílení komponent • Sdílet komponenty např. kvůli úspoře paměti • Složitější, když komponenta může mít nejvýše jednoho rodiče • Řešení – děti mají více rodičů - DAG • Problémy s víceznačností, když je požadavek propagován směrem nahoru ve struktuře
Příklad class Equipment { protected: string name; public: Equipment(string name):name(name) {} virtual ~Equipment() {} virtual void add(Equipment* equipment) =0; virtual void remove(Equipment* equipment) =0; virtual int getTotalPrice() =0; string getName() {return this->name;} }; Component (abstract child methods) class Keyboard : public Equipment { private: int price; public: Keyboard(string name, int price) : Equipment(name), price(price) {} int getTotalPrice() {return this->price;} void add(Equipment*) { throw RtExc(); } void remove(Equipment*) { throw RtExc(); } }; Leaf (not implemented child methods)
Příklad Composite class PC : public Equipment { private: List<Equipment*> children; public: PC(String name) : Equipment(name) {}; ~PC() { for (Equipment* equipment : children) delete equipment; } int getTotalPrice() { int total = 0; for (Equipment* equipment : children) total += equipment->getTotalPrice(); return total; } void add(Equipment* equipment) { children.add(equipment); } void remove(Equipment* equipment) {children.remove(equipment);} }; Využití potomků pro výpočet celkové částky Implementované child methods
Použití • GUI • Komponenty-kontejnery na komponenty • Obecné (Panel, Container) • Speciální (menu, strom) • AWT, Swing • Reprezentace XML • Reprezentacearitmetického výrazu • Nebo programu (složený příkaz) • Struktura souborů a adresářů java.awt
Související vzory • Command • Složený MacroCommand • Decorator • Často používán s Composite • Interface třídy Component z Decoratoru navíc obsahuje metody pro práci s dětmi • Flyweight • Sdílení komponent • Iterator • Procházení potomků různými způsoby • Visitor • Lokalizuje operace a chování, které by jinak byly rozprostřeny v Composite i Leaf třídách
Shrnutí • Zakrytí rozdílu mezi objektem a kolekcí těchto objektů • Zjednodušení práce klienta • Možnost cacheování výsledků získaných z potomků • Urychlení běhu programu • Struktura
Composite • Zdroje • Příklady použití • http://www.javaworld.com/javaworld/jw-09-2002/jw-0913-designpatterns.html • http://sourcemaking.com/design_patterns/composite • http://www.vincehuston.org/dp/composite.html • http://www.coolapps.net/composite.htm • http://composing-the-semantic-web.blogspot.com/2007/07/composite-design-pattern-in-rdfowl.html • Vše ostatní • GoF • C# návrhové vzory, Judith Bishopová