Decorator
Problém Káva Cena House Blend 3.00€ Kenya 1.50€ Decaf Veranda Blend 3.50€ Espresso 2.00€
Problém Káva Cena House Blend 3.00€ Kenya 1.50€ Decaf Veranda Blend 3.50€ Espresso 2.00€ Šlehačka Čokoláda Mléko Voda Sojové mléko Mléčná pěna Čokoládový sirup Whiskey
„Řešení“ číslo 1 Omezení na podmnožinu Nemožné vytvořit jakoukoliv kombinaci Co když se změní cena mléka?
„Řešení“ číslo 2 Dědičnost
„Řešení“ číslo 2 Dědičnost
„Řešení“ číslo 3 Supertřída
„Řešení“ číslo 3 Supertřída
„Řešení“ číslo 3 Supertřída
„Řešení“ číslo 3 Supertřída if (MaMleko()) { cena += 0.10; } if (MaSojoveMleko()) cena += 0.50; if (MaPenu()) cena += 0.25; if (MaCokoladu()) cena += 0.65;
„Řešení“ číslo 3 Supertřída Změna ceny doplňku vynutí změnu celého kódu Přidání doplňku znamená změnu supertřída Co když chceme dvojitou dávku mléka?
„Open-Closed“ princip Třídy by měly být: otevřené pro rozšíření zavřené pro modifikaci Možnost rozšířit funkcionalitu bez nutnosti měnit stávající kód
Dědičnost ... je silný nástroj... ale může vést k neflexibilnímu designu vytváří statické, compile-time rozhodnutí
Kompozice stále umožňuje "dědit" chování umožňuje dynamické, run-time rozhodnutí umožňuje přidat nové chování bez změny stávajícího kódu
Kompozice větší flexibilita méně chyb a vedlejších efektů
Řešení číslo 4 Finální Espresso Cena()
Řešení číslo 4 Finální Postupně přidáváme doplňky/“dekorace“ Espresso Mléko Cena() Espresso Cena() Postupně přidáváme doplňky/“dekorace“
Řešení číslo 4 Finální Decorator a.k.a. Wrapper Espresso Cena() Mléko Šlehačka Cena() Mléko Cena() Espresso Cena() Decorator a.k.a. Wrapper
Řešení číslo 4 Finální Získání finální ceny Cena = 2 Espresso Cena() Šlehačka Cena() Mléko Cena() Espresso Cena() Cena = 2 Získání finální ceny Cena = <cena vyšší vrstvy> + 0.1 Cena = <cena vyšší vrstvy> + 0.3 Cena()
Řešení číslo 4 Finální Získání finální ceny Cena = 2 Espresso Cena() Šlehačka Cena() Mléko Cena() Espresso Cena() Cena = 2 Získání finální ceny Cena = <cena vyšší vrstvy> + 0.1 Cena = <cena vyšší vrstvy> + 0.3 Cena()
Vzor dekorátor ... přidává dodatečné závazky do objektu dynamicky Dekorátory poskytují flexibilní alternativu k podtřídám pro rozšíření funkcionality
Diagram
Společný abstraktní předek Diagram Společný abstraktní předek Abstraktní dekorátor Konkrétní komponenta Konkrétní dekorátor
Diagram
Kód
Kód
Kód
Vzor dekorátor Můžeme: libovolně kombinovat dekorátory přidávat více stejných dekorátorů k jedné komponentě
Potenciální problémy Pevné hodnoty (ceny) pro určité kombinace neodpovídají ručně vyrobené kombinaci Zaokrouhlování cen Možnost "negativní hodnoty" (odečtu ceny) v dekorátoru
Reálný problém InputStream FileInputStream InputFileLineStream LineInputStream FileInputStream InputStream InputFileLineStream
Reálné využití Grafické toolkity Čtení vstupu, zápis výstupu Java Swing System.Windows.Controls Čtení vstupu, zápis výstupu System.IO.Stream java.io DataInputStream dis = new DataInputStream( new GzipInputStream( new BufferedInputStream( new FileInputStream("file.gz")))); dis.Read(...);
Vestavěná podpora v jazycích zavináč @ JavaScript TypeScript Python
Výhody Vyšší flexibilita pro přidávání funkcionality oproti statickému dědění Transparentnost - programátor stále vidí objekt typu Nápoj Možnost rozšíření - přidávání doplňků Odolnost vůči změnám
Nevýhody Komponenta a dekorovaná verze komponentu nejsou identické Pena(Mleko(Espresso)) == Karamel(Decaf) Pena(Mleko(Espresso)).Cena() != Karamel(Decaf).Cena() Dlouhá řetěz dekorátorů => dopad na výkon Velké množství malých tříd
Související vzory Adapter Strategy Composite Decorator mění pouze chování objektu, ne jeho rozhraní Adapter dá objektu zcela nové rozhraní Composite Decorator je modifikovaný Composite - s jedinou komponentou Decorator přidává dodatečné chování, neagreguje objekty Strategy Decorator mění povrch objektu / Strategy mění vnitřek objektu Komponenta ve Strategy ví o extenzích