| Computer Science Design Patterns Decorator | Facade |
The decorator pattern helps to add behavior or responsibilities to an object. This is also called “Wrapper”.
To do: |
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.
This pattern is expensive to create.
This pattern can be expensive to maintain. If the representation of a class often changes, you will have lots of refactoring.
This pattern is hard to remove too.
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;}}
#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
// 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());
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
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.
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
# 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()
# 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()
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
The decorator pattern can also be implemented in dynamic languages either with interfaces or with traditional OOP inheritance.
To do: |
| Computer Science Design Patterns Decorator | Facade |