160 likes | 285 Views
Decorator. Motivační příklad – GUI Toolkit. GUI Toolkit obsahuje komponentu TextView prosté zobrazení textu Chceme instance této komponenty „dekorovat“ přidat posuvník přidat rámeček apod. Hodilo by se přitom: mít k dispozici také původní TextView bez dekorací
E N D
Motivační příklad – GUI Toolkit • GUI Toolkit obsahuje komponentu TextView • prosté zobrazení textu • Chceme instance této komponenty „dekorovat“ • přidat posuvník • přidat rámeček • apod. • Hodilo by se přitom: • mít k dispozici také původní TextView • bez dekorací • kombinovat vlastnosti mezi sebou • posuvník + rámeček • přidávat některé vlastnosti vícenásobně • dvojitý rámeček • přidávat/odebírat konkrétní vlastnosti za běhu
Motivační příklad – GUI Toolkit • 1. pokus o řešení: „supertřída“ • obsahuje stavy (flagy) pro všechny možné dekorace • vykreslovací metoda kontroluje přítomnost jednotlivých dekorací • class TextView • { • public: • void Draw() • { • /* Main drawing logic here. */ • if (isBordered) • { • /* Border drawing logic here. */ • } • if (isScrollable) • { • /* Scrollbar drawing logic here. */ • } • } • private: • bool isBordered; // Is TextView bordered? • int borderWidth; // State used only by bordered TextViews. • bool isScrollable; // Is TextView scrollable? • intscrollBarPosition; // State used only by scrollable TextViews. • };
Motivační příklad – GUI Toolkit • 1. pokus o řešení: „supertřída“ • můžeme instancím přidávat/odebírat vlastnosti za běhu • nemusíme dekorovat vůbec • můžeme kombinovat vlastnosti • netřeba rozlišovat mezi obyčejnou a dekorovanou instancí • nemůžeme jednu vlastnost použít vícekrát • instance ví, že je dekorována • silně neflexibilní řešení • nerozšiřitelné bez modifikace TextView
Motivační příklad – GUI Toolkit • 2. pokus o řešení: dědičnost • bázová třída – TextView • odvozené – BorderedTextView, ScrollableTextView, BorderedScrollableTextView • class TextView { • public: • void Draw() { ... } • }; • class BorderedTextView : public TextView { • public: • void Draw() { ... } • }; • class ScrollableTextView : public TextView { • public: • void Draw() { ... } • }; • class BorderedScrollableTextView : public ScrollableTextView { • public: • void Draw() { ... } • };
Motivační příklad – GUI Toolkit • 2. pokus o řešení: dědičnost • statické • nemožno měnit vlastnosti instance za běhu • pro každou kombinaci vlastností je třeba vytvořit novou třídu • BorderedScrollableBorderedTextView ... • vede k explozi tříd (n vlastností → 2n tříd) • podstatně zvyšuje komplexitu systému
Motivační příklad – GUI Toolkit • 3. pokus o řešení: Decorator Společný (abstraktní) předek – def. interface pro kreslení Konkrétní vizuální komponenta Reference na dekorovaný objekt Delegace vykreslování na dekorovaný objekt Abstraktní dekorátor Konkrétní dekorátory vyjma delegace implementují navíc své vlastní vykreslování Konkrétní dekorátor
Motivační příklad – GUI Toolkit • 3. pokus o řešení: Decorator class VisualComponent { public: virtual void Draw() = 0; }; class TextView : public VisualComponent { public: virtual void Draw(){ ... } }; class Decorator : public VisualComponent { public: Decorator(VisualComponent * vc) { _vc = vc; } virtual void Draw() { _vc->Draw(); } private: VisualComponent* _vc; }; class ScrollDecorator : public Decorator { public: ScrollDecorator(VisualComponent *vc) : Decorator(vc){} virtual void Draw() { Decorator::Draw();// draw component DrawScrollbar(); // draw scrollbar } void ScrollTo(int pos) { scroll_position = pos; } private: int scroll_position; }; class BorderDecorator : public Decorator { ... };
Motivační příklad – GUI Toolkit • 3. pokus o řešení: Decorator • dekorátory přidávají jednotlivé vlastnosti (dekorace) • můžeme instancím přidávat/odebírat vlastnosti za běhu • máme k dispozici i obyčejný TextView • dekorace jsou navzájem nezávislé • lze je libovolně kombinovat • lze je používat i vícekrát • je to transparentní • z hlediska klienta není rozdíl mezi obyčejným a dekorovaným TextView • TextView o dekoracích vůbec neví VisualComponent * visualComponent= new BorderDecorator( new ScrollDecorator( new TextView())); visualComponent->Draw();
Decorator – struktura a účastníci • Účastníci: • Component – def. rozhraní pro objekty, které je možné dynamicky rozšiřovat • ConcreteComponent – def. objekt, který je možné dynamicky rozšířit • Decorator – def. rozhraní pro všechny dekorátory • obsahuje refenci na objekt, který dekoruje • všechna volání deleguje na dekorovaný objekt • ConcreteDecorator – přidává dodatečné chování komponentě
Decorator – o čem to je • Rozšiřuje objekty o dodatečné chování • rozšiřuje konkrétní objekty, ne třídy • rozšiřuje objekt dynamicky, tj. za běhu • Upřednostňuje kompozici objektů před dědičností • Delegace volání na dekorovaný objekt + vlastní přidaná funkcionalita • vlastní přidané chování může být před i za delegovaným voláním • Použití typicky pomocí řetězení konstruktorů: Component component = new ConcreteDecoratorA( new ConcreteDecoratorB( new ConcreteComponent(...))); component.Operation();
Decorator – výhody a nevýhody • Výhody • vyšší flexibilita pro přidávání funkcionality než při statickém dědění • vlastnosti lze přidávat/odebírat za běhu aplikace • několikanásobné použití stejné dekorace • transparentnost • „pay-as-you-go“ • není třeba předvídat všechny potřeby klienta • jednoduché inkrementální přidávání funkcionality • Nevýhody • komponenta a její dekorovaná verze nejsou identické • dekorátor se chová jako transparentní zapouzdření • při použití dekorátorů nespoléhat na identitu objektů! • mnoho podobných (malých) objektů • potenciálně horší orientace v kódu
Decorator – implementace • Rozhraní dekorátoru musí být shodné s rozhraním dekorovaného objektu • dědění od společného předka nebo implementace společného interface • Abstraktní dekorátor lze vynechat • za předpokladu, že potřebujeme přidat pouze jediné rozšíření • často v případě, kdy potřebujeme rozšířit existující kód • delegování na komponentu se pak děje přímo v tomto jediném dekorátoru • Společný předek (Component) by měl zůstat odlehčený • definice rozhraní, nikoli uložení dat • jinak hrozí, že dekorátory budou příliš těžkotonážní • Dobré zvážit, zda měnit povrch objektu, či jeho vnitřnosti • návrhový vzor Strategy
Reálně využívané Decoratory • Grafické toolkity • X Windows System • Java Swing • System.Windows.Controls • Čtení vstupu, zapisování výstupu • System.IO.Stream • java.io Component ConcreteComponent Decorator DataInputStreamdis = new DataInputStream( new GzipInputStream( new BufferedInputStream( new FileInputStream("file.gz")))); dis.Read(...); ConcreteDecorator
Související vzory • Adapter • Decorator mění pouze chování objektu, ne jeho rozhraní • Adapter dá objektu zcela nové rozhraní • Composite • Decorator lze technicky vnímat jak zdegenerovaný Composite • s jedinou komponentou • Decorator přidává dodatečné chování • není určen pro agregaci objektů • Strategy • Decorator umožňuje měnit skin (povrch, kůži...) objektu • v zásadě obaluje nějaký objekt a mění při tom jeho chování • zatímco Strategy umožňuje měnit „vnitřnosti“ (guts) objektu • komponenta ve Strategy ví o možných extenzích, u Decoratoru nikoli:
Děkuji za pozornost! Q & A