Decorator 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.
Download
Report
Transcript Decorator 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.
Decorator
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
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
kombinovat vlastnosti mezi sebou
posuvník + rámeček
přidávat některé vlastnosti vícenásobně
bez dekorací
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
private
private
private
}
bool isBordered;
int borderWidth;
bool isScrollable;
int scrollBarPosition;
//
//
//
//
Is TextView bordered?
Used only by bordered TextViews.
Is TextView scrollable?
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 extends TextView {
public void Draw() { ... }
}
class ScrollableTextView extends TextView {
public void Draw() { ... }
}
class BorderedScrollableTextView extends 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átor
Konkrétní dekorátory vyjma
delegace implementují navíc
své vlastní vykreslování
Motivační příklad – GUI Toolkit
3. pokus o řešení: Decorator
public interface VisualComponent
{
public void Draw();
}
class TextView implements VisualComponent
{
public void Draw() {
// draw window
}
}
abstract class Decorator implements
VisualComponent
{
protected VisualComponent vc;
// component being decorated
public Decorator(VisualComponent vc) {
this.vc = vc;
}
public void Draw() {
vc.draw(); //delegation
}
}
class ScrollDecorator extends Decorator {
public ScrollDecorator(VisualComponent
vc) {
super(vc);
}
@Override
public void Draw() {
super.Draw();
drawScrollBar();
}
// draw component
// draw scrollbar
private void drawScrollBar() {...}
private int scroll_position;
public void ScrollTo(int pos)
{
scroll_position = pos;
}
}
class BorderDecorator extends Decorator {
...
}
Motivační příklad – GUI Toolkit
3. pokus o řešení: Decorator
VisualComponent window =
new BorderDecorator(
new ScrollDecorator(
new TextView()));
window.Draw();
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í
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 – 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
Použití typicky pomocí řetězení konstruktorů:
Component c =
new ConcreteDecoratorA(
new ConcreteDecoratorB(
new ConcreteComponent(...)));
c.Operation();
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
Decorator
ConcreteComponent
DataInputStream dis =
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
Decorator přidává dodatečné chování
s jedinou komponentou
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: