Movatterモバイル変換


[0]ホーム

URL:


Jump to content
WikibooksThe Free Textbook Project
Search

Decorator

25% developed
From Wikibooks, open books for an open world
<Computer Science Design Patterns

CompositeComputer Science Design Patterns
Decorator
Facade
Table of content

The decorator pattern helps to add behavior or responsibilities to an object. This is also called “Wrapper”.

Examples

Clipboard

To do:
Find examples.


Cost

This pattern can be very expensive. You should only use it when it is really necessary. You should have lots of different behaviors and responsibilities for the same class.

Creation

This pattern is expensive to create.

Maintenance

This pattern can be expensive to maintain. If the representation of a class often changes, you will have lots of refactoring.

Removal

This pattern is hard to remove too.

Advises

  • Put thedecorator term in the name of the decorator classes to indicate the use of the pattern to the other developers.

Implementations

Implementation in C#

This example illustrates a simple extension method for a bool type.

usingSystem;// Extension methods must be parts of static classes.staticclassBooleanExtensionMethodSample{publicstaticvoidMain(){boolyes=true;boolno=false;// Toggle the booleans! yes should return false and no should return true.Console.WriteLine(yes.Toggle());Console.WriteLine(no.Toggle());}// The extension method that adds Toggle to bool.publicstaticboolToggle(thisbooltarget){// Return the opposite of the target.return!target;}}
Implementation in C++

Coffee making scenario

#include<iostream>#include<string>// The abstract coffee classclassCoffee{public:virtualdoublegetCost()=0;virtualstd::stringgetIngredient()=0;virtual~Coffee(){}};// Plain coffee without ingredientclassSimpleCoffee:publicCoffee{private:doublecost;std::stringingredient;public:SimpleCoffee(){cost=1;ingredient=std::string("Coffee");}doublegetCost(){returncost;}std::stringgetIngredient(){returningredient;}};// Abstract decorator classclassCoffeeDecorator:publicCoffee{protected:Coffee&decoratedCoffee;public:CoffeeDecorator(Coffee&decoratedCoffee):decoratedCoffee(decoratedCoffee){}~CoffeeDecorator(){delete&decoratedCoffee;}};// Milk DecoratorclassMilk:publicCoffeeDecorator{private:doublecost;public:Milk(Coffee&decoratedCoffee):CoffeeDecorator(decoratedCoffee){cost=0.5;}doublegetCost(){returncost+decoratedCoffee.getCost();}std::stringgetIngredient(){return"Milk "+decoratedCoffee.getIngredient();}};// Whip decoratorclassWhip:publicCoffeeDecorator{private:doublecost;public:Whip(Coffee&decoratedCoffee):CoffeeDecorator(decoratedCoffee){cost=0.7;}doublegetCost(){returncost+decoratedCoffee.getCost();}std::stringgetIngredient(){return"Whip "+decoratedCoffee.getIngredient();}};// Sprinkles decoratorclassSprinkles:publicCoffeeDecorator{private:doublecost;public:Sprinkles(Coffee&decoratedCoffee):CoffeeDecorator(decoratedCoffee){cost=0.6;}doublegetCost(){returncost+decoratedCoffee.getCost();}std::stringgetIngredient(){return"Sprinkles "+decoratedCoffee.getIngredient();}};// Here's a testintmain(){Coffee*sample;sample=newSimpleCoffee();sample=newMilk(*sample);sample=newWhip(*sample);std::cout<<sample->getIngredient()<<std::endl;std::cout<<"Cost: "<<sample->getCost()<<std::endl;deletesample;}

The output of this program is given below:

Whip Milk Coffee
Cost: 2.2

Implementation in JavaScript
// Class to be decoratedfunctionCoffee(){this.cost=function(){return1;};}// Decorator AfunctionMilk(coffee){this.cost=function(){returncoffee.cost()+0.5;};}// Decorator BfunctionWhip(coffee){this.cost=function(){returncoffee.cost()+0.7;};}// Decorator CfunctionSprinkles(coffee){this.cost=function(){returncoffee.cost()+0.2;};}// Here's one way of using itvarcoffee=newMilk(newWhip(newSprinkles(newCoffee())));alert(coffee.cost());// Here's anothervarcoffee=newCoffee();coffee=newSprinkles(coffee);coffee=newWhip(coffee);coffee=newMilk(coffee);alert(coffee.cost());
Implementation in Kotlin
funprintInfo(c:Coffee){println("Cost: "+c.cost+"; Ingredients: "+c.ingredients)}funmain(args:Array<String>){varc:Coffee=SimpleCoffee()printInfo(c)c=WithMilk(c)printInfo(c)c=WithSprinkles(c)printInfo(c)}// The interface Coffee defines the functionality of Coffee implemented by decoratorinterfaceCoffee{valcost:Double// Returns the cost of the coffeevalingredients:String// Returns the ingredients of the coffee}// Extension of a simple coffee without any extra ingredientsclassSimpleCoffee:Coffee{overridevalcost:Doubleget()=1.0overridevalingredients:Stringget()="Coffee"}// Abstract decorator class - note that it implements Coffee interfaceabstractclassCoffeeDecorator(privatevaldecoratedCoffee:Coffee):Coffee{override// Implementing methods of the interfacevalcost:Doubleget()=decoratedCoffee.costoverridevalingredients:Stringget()=decoratedCoffee.ingredients}// Decorator WithMilk mixes milk into coffee.// Note it extends CoffeeDecorator.classWithMilk(c:Coffee):CoffeeDecorator(c){override// Overriding methods defined in the abstract superclassvalcost:Doubleget()=super.cost+0.5overridevalingredients:Stringget()=super.ingredients+", Milk"}// Decorator WithSprinkles mixes sprinkles onto coffee.// Note it extends CoffeeDecorator.classWithSprinkles(c:Coffee):CoffeeDecorator(c){overridevalcost:Doubleget()=super.cost+0.2overridevalingredients:Stringget()=super.ingredients+", Sprinkles"}

The output of this program is given below:

Cost: 1.0; Ingredients: Coffee
Cost: 1.5; Ingredients: Coffee, Milk
Cost: 1.7; Ingredients: Coffee, Milk, Sprinkles

Implementation in Java

First Example (window/scrolling scenario)

The following Java example illustrates the use of decorators using the window/scrolling scenario.

// the Window abstract classpublicabstractclassWindow{publicabstractvoiddraw();// draws the WindowpublicabstractStringgetDescription();// returns a description of the Window}// extension of a simple Window without any scrollbarsclassSimpleWindowextendsWindow{publicvoiddraw(){// draw window}publicStringgetDescription(){return"simple window";}}

The following classes contain the decorators for allWindow classes, including the decorator classes themselves.

// abstract decorator class - note that it extends WindowabstractclassWindowDecoratorextendsWindow{protectedWindowdecoratedWindow;// the Window being decoratedpublicWindowDecorator(WindowdecoratedWindow){this.decoratedWindow=decoratedWindow;}publicvoiddraw(){decoratedWindow.draw();//delegation}publicStringgetDescription(){returndecoratedWindow.getDescription();//delegation}}// the first concrete decorator which adds vertical scrollbar functionalityclassVerticalScrollBarDecoratorextendsWindowDecorator{publicVerticalScrollBarDecorator(WindowdecoratedWindow){super(decoratedWindow);}@Overridepublicvoiddraw(){super.draw();drawVerticalScrollBar();}privatevoiddrawVerticalScrollBar(){// draw the vertical scrollbar}@OverridepublicStringgetDescription(){returnsuper.getDescription()+", including vertical scrollbars";}}// the second concrete decorator which adds horizontal scrollbar functionalityclassHorizontalScrollBarDecoratorextendsWindowDecorator{publicHorizontalScrollBarDecorator(WindowdecoratedWindow){super(decoratedWindow);}@Overridepublicvoiddraw(){super.draw();drawHorizontalScrollBar();}privatevoiddrawHorizontalScrollBar(){// draw the horizontal scrollbar}@OverridepublicStringgetDescription(){returnsuper.getDescription()+", including horizontal scrollbars";}}

Here's a test program that creates aWindow instance which is fully decorated (i.e., with vertical and horizontal scrollbars), and prints its description:

publicclassDecoratedWindowTest{publicstaticvoidmain(String[]args){// create a decorated Window with horizontal and vertical scrollbarsWindowdecoratedWindow=newHorizontalScrollBarDecorator(newVerticalScrollBarDecorator(newSimpleWindow()));// print the Window's descriptionSystem.out.println(decoratedWindow.getDescription());}}

The output of this program is "simple window, including vertical scrollbars, including horizontal scrollbars". Notice how thegetDescription method of the two decorators first retrieve the decoratedWindow's description anddecorates it with a suffix.

Second Example (coffee making scenario)

The next Java example illustrates the use of decorators using coffee making scenario.In this example, the scenario only includes cost and ingredients.

// The abstract Coffee class defines the functionality of Coffee implemented by decoratorpublicabstractclassCoffee{publicabstractdoublegetCost();// returns the cost of the coffeepublicabstractStringgetIngredients();// returns the ingredients of the coffee}// extension of a simple coffee without any extra ingredientspublicclassSimpleCoffeeextendsCoffee{publicdoublegetCost(){return1;}publicStringgetIngredients(){return"Coffee";}}

The following classes contain the decorators for allCoffee classes, including the decorator classes themselves..

// abstract decorator class - note that it extends Coffee abstract classpublicabstractclassCoffeeDecoratorextendsCoffee{protectedfinalCoffeedecoratedCoffee;protectedStringingredientSeparator=", ";publicCoffeeDecorator(CoffeedecoratedCoffee){this.decoratedCoffee=decoratedCoffee;}publicdoublegetCost(){// implementing methods of the abstract classreturndecoratedCoffee.getCost();}publicStringgetIngredients(){returndecoratedCoffee.getIngredients();}}// Decorator Milk that mixes milk with coffee// note it extends CoffeeDecoratorclassMilkextendsCoffeeDecorator{publicMilk(CoffeedecoratedCoffee){super(decoratedCoffee);}publicdoublegetCost(){// overriding methods defined in the abstract superclassreturnsuper.getCost()+0.5;}publicStringgetIngredients(){returnsuper.getIngredients()+ingredientSeparator+"Milk";}}// Decorator Whip that mixes whip with coffee// note it extends CoffeeDecoratorclassWhipextendsCoffeeDecorator{publicWhip(CoffeedecoratedCoffee){super(decoratedCoffee);}publicdoublegetCost(){returnsuper.getCost()+0.7;}publicStringgetIngredients(){returnsuper.getIngredients()+ingredientSeparator+"Whip";}}// Decorator Sprinkles that mixes sprinkles with coffee// note it extends CoffeeDecoratorclassSprinklesextendsCoffeeDecorator{publicSprinkles(CoffeedecoratedCoffee){super(decoratedCoffee);}publicdoublegetCost(){returnsuper.getCost()+0.2;}publicStringgetIngredients(){returnsuper.getIngredients()+ingredientSeparator+"Sprinkles";}}

Here's a test program that creates aCoffee instance which is fully decorated (i.e., with milk, whip, sprinkles), and calculate cost of coffee and prints its ingredients:

publicclassMain{publicstaticfinalvoidmain(String[]args){Coffeec=newSimpleCoffee();System.out.println("Cost: "+c.getCost()+"; Ingredients: "+c.getIngredients());c=newMilk(c);System.out.println("Cost: "+c.getCost()+"; Ingredients: "+c.getIngredients());c=newSprinkles(c);System.out.println("Cost: "+c.getCost()+"; Ingredients: "+c.getIngredients());c=newWhip(c);System.out.println("Cost: "+c.getCost()+"; Ingredients: "+c.getIngredients());// Note that you can also stack more than one decorator of the same typec=newSprinkles(c);System.out.println("Cost: "+c.getCost()+"; Ingredients: "+c.getIngredients());}}

The output of this program is given below:

Cost: 1.0 Ingredient: Coffee
Cost: 1.5 Ingredient: Coffee, Milk
Cost: 1.7 Ingredient: Coffee, Milk, Sprinkles
Cost: 2.4 Ingredient: Coffee, Milk, Sprinkles, Whip

Implementation in Python

Window System

# the Window base classclassWindow(object):defdraw(self,device):device.append('flat window')definfo(self):pass# The decorator pattern approchclassWindowDecorator:def__init__(self,w):self.window=wdefdraw(self,device):self.window.draw(device)definfo(self):self.window.info()classBorderDecorator(WindowDecorator):defdraw(self,device):self.window.draw(device)device.append('borders')classScrollDecorator(WindowDecorator):defdraw(self,device):self.window.draw(device)device.append('scroll bars')deftest_deco():# The way of using the decorator classesw=ScrollDecorator(BorderDecorator(Window()))dev=[]w.draw(dev)printdevtest_deco()

Difference between subclass method and decorator pattern

# The subclass approchclassBorderedWindow(Window):defdraw(self,device):super(BorderedWindow,self).draw(device)device.append('borders')classScrolledWindow(Window):defdraw(self,device):super(ScrolledWindow,self).draw(device)device.append('scroll bars')# combine the functionalities using multiple inheritance.classMyWindow(ScrolledWindow,BorderedWindow,Window):passdeftest_muli():w=MyWindow()dev=[]w.draw(dev)printdevdeftest_muli2():# note that python can create a class on the fly.MyWindow=type('MyWindow',(ScrolledWindow,BorderedWindow,Window),{})w=MyWindow()dev=[]w.draw(dev)printdevtest_muli()test_muli2()
Implementation in F#

Coffee example

typeCoffee()=abstractmemberCost:doubleabstractmemberIngredients:stringlistdefaultthis.Cost=1.0defaultthis.Ingredients=["Coffee"]typeCoffeeDecorator(coffee:Coffee)=inheritCoffee()overridethis.Cost=coffee.Costoverridethis.Ingredients=coffee.IngredientstypeWithMilk(coffee:Coffee)=inheritCoffeeDecorator(coffee)overridethis.Cost=base.Cost+0.5overridethis.Ingredients=["Milk"]|>List.appendbase.IngredientstypeWithSprinkles(coffee:Coffee)=inheritCoffeeDecorator(coffee)overridethis.Cost=base.Cost+0.2overridethis.Ingredients=["Sprinkles"]|>List.appendbase.Ingredientsletprint(coffee:Coffee)=printfn"Cost: %.2f$; Ingredients: %A"coffee.Costcoffee.IngredientsletwithAddins=Coffee()|>WithMilk|>WithSprinklesprintwithAddins

Dynamic languages

The decorator pattern can also be implemented in dynamic languages either with interfaces or with traditional OOP inheritance.

External links


Clipboard

To do:
Add more illustrations.


CompositeComputer Science Design Patterns
Decorator
Facade


You have questions about this page?
Ask it here:


Create a new page on this book:


Retrieved from "https://en.wikibooks.org/w/index.php?title=Computer_Science_Design_Patterns/Decorator&oldid=3675864"
Category:
Hidden category:

[8]ページ先頭

©2009-2025 Movatter.jp