Decorator
Various examples of the decorator pattern.
This example illustrates a simple extension method for a bool type.
using System; static class BooleanExtensionMethodSample { public static void Main() { bool yes = true; bool no = 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. public static bool Toggle(this bool target) { // Evaluate the input and then return the opposite value. if (target) return false; else return true; // Satisfy the compiler in case of a null value. } }
First Example (window/scrolling scenario)
The following Java example illustrates the use of decorators using the window/scrolling scenario.
// the Window abstract class public abstract class Window { public abstract void draw(); // draws the Window public abstract String getDescription(); // returns a description of the Window } // extension of a simple Window without any scrollbars class SimpleWindow extends Window { public void draw() { // draw window } public String getDescription() { return "simple window"; } }
The following classes contain the decorators for all Window classes, including the decorator classes themselves.
// abstract decorator class - note that it extends Window abstract class WindowDecorator extends Window { protected Window decoratedWindow; // the Window being decorated public WindowDecorator (Window decoratedWindow) { this.decoratedWindow = decoratedWindow; } public void draw() { decoratedWindow.draw(); //delegation } public String getDescription() { return decoratedWindow.getDescription(); //delegation } } // the first concrete decorator which adds vertical scrollbar functionality class VerticalScrollBarDecorator extends WindowDecorator { public VerticalScrollBarDecorator (Window decoratedWindow) { super(decoratedWindow); } @Override public void draw() { super.draw(); drawVerticalScrollBar(); } private void drawVerticalScrollBar() { // draw the vertical scrollbar } @Override public String getDescription() { return super.getDescription() + ", including vertical scrollbars"; } } // the second concrete decorator which adds horizontal scrollbar functionality class HorizontalScrollBarDecorator extends WindowDecorator { public HorizontalScrollBarDecorator (Window decoratedWindow) { super(decoratedWindow); } @Override public void draw() { super.draw(); drawHorizontalScrollBar(); } private void drawHorizontalScrollBar() { // draw the horizontal scrollbar } @Override public String getDescription() { return super.getDescription() + ", including horizontal scrollbars"; } }
Here's a test program that creates a Window instance which is fully decorated (i.e., with vertical and horizontal scrollbars), and prints its description:
public class DecoratedWindowTest { public static void main(String[] args) { // create a decorated Window with horizontal and vertical scrollbars Window decoratedWindow = new HorizontalScrollBarDecorator ( new VerticalScrollBarDecorator(new SimpleWindow())); // print the Window's description System.out.println(decoratedWindow.getDescription()); } }
The output of this program is "simple window, including vertical scrollbars, including horizontal scrollbars". Notice how the getDescription method of the two decorators first retrieve the decorated Window's description and decorates 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 decorator public abstract class Coffee { public abstract double getCost(); // returns the cost of the coffee public abstract String getIngredients(); // returns the ingredients of the coffee } // extension of a simple coffee without any extra ingredients public class SimpleCoffee extends Coffee { public double getCost() { return 1; } public String getIngredients() { return "Coffee"; } }
The following classes contain the decorators for all Coffee classes, including the decorator classes themselves..
// abstract decorator class - note that it extends Coffee abstract class public abstract class CoffeeDecorator extends Coffee { protected final Coffee decoratedCoffee; protected String ingredientSeparator = ", "; public CoffeeDecorator(Coffee decoratedCoffee) { this.decoratedCoffee = decoratedCoffee; } public double getCost() { // implementing methods of the abstract class return decoratedCoffee.getCost(); } public String getIngredients() { return decoratedCoffee.getIngredients(); } } // Decorator Milk that mixes milk with coffee // note it extends CoffeeDecorator class Milk extends CoffeeDecorator { public Milk(Coffee decoratedCoffee) { super(decoratedCoffee); } public double getCost() { // overriding methods defined in the abstract superclass return super.getCost() + 0.5; } public String getIngredients() { return super.getIngredients() + ingredientSeparator + "Milk"; } } // Decorator Whip that mixes whip with coffee // note it extends CoffeeDecorator class Whip extends CoffeeDecorator { public Whip(Coffee decoratedCoffee) { super(decoratedCoffee); } public double getCost() { return super.getCost() + 0.7; } public String getIngredients() { return super.getIngredients() + ingredientSeparator + "Whip"; } } // Decorator Sprinkles that mixes sprinkles with coffee // note it extends CoffeeDecorator class Sprinkles extends CoffeeDecorator { public Sprinkles(Coffee decoratedCoffee) { super(decoratedCoffee); } public double getCost() { return super.getCost() + 0.2; } public String getIngredients() { return super.getIngredients() + ingredientSeparator + "Sprinkles"; } }
Here's a test program that creates a Coffee instance which is fully decorated (i.e., with milk, whip, sprinkles), and calculate cost of coffee and prints its ingredients:
public class Main { public static final void main(String[] args) { Coffee c = new SimpleCoffee(); System.out.println("Cost: " + c.getCost() + "; Ingredients: " + c.getIngredients()); c = new Milk(c); System.out.println("Cost: " + c.getCost() + "; Ingredients: " + c.getIngredients()); c = new Sprinkles(c); System.out.println("Cost: " + c.getCost() + "; Ingredients: " + c.getIngredients()); c = new Whip(c); System.out.println("Cost: " + c.getCost() + "; Ingredients: " + c.getIngredients()); // Note that you can also stack more than one decorator of the same type c = new Sprinkles(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
Coffee making scenario
# include <iostream> # include <string> // The abstract coffee class class Coffee { public: virtual double getCost() = 0; virtual std::string getIngredient() = 0; }; // Plain coffee without ingredient class SimpleCoffee:public Coffee { private: double cost; std::string ingredient; public: SimpleCoffee() { cost = 1; ingredient = std::string("Coffee"); } double getCost() { return cost; } std::string getIngredient() { return ingredient; } }; // Abstract decorator class class CoffeeDecorator:public Coffee { protected: Coffee & decoratedCoffee; public: CoffeeDecorator(Coffee & decoratedCoffee):decoratedCoffee(decoratedCoffee){} }; // Milk Decorator class Milk:public CoffeeDecorator { private: double cost; public: Milk(Coffee & decoratedCoffee):CoffeeDecorator(decoratedCoffee) { cost = 0.5; } double getCost() { return cost + decoratedCoffee.getCost(); } std::string getIngredient() { return "Milk "+decoratedCoffee.getIngredient(); } }; // Whip decorator class Whip:public CoffeeDecorator { private: double cost; public: Whip(Coffee & decoratedCoffee):CoffeeDecorator(decoratedCoffee) { cost = 0.7; } double getCost() { return cost + decoratedCoffee.getCost(); } std::string getIngredient() { return "Whip "+decoratedCoffee.getIngredient(); } }; // Sprinkles decorator class Sprinkles:public CoffeeDecorator { private: double cost; public: Sprinkles(Coffee & decoratedCoffee):CoffeeDecorator(decoratedCoffee) { cost = 0.6; } double getCost() { return cost + decoratedCoffee.getCost(); } std::string getIngredient() { return "Sprinkles "+decoratedCoffee.getIngredient(); } }; // Here's a test int main() { Coffee* sample; sample = new SimpleCoffee(); sample = new Milk(*sample); sample = new Whip(*sample); std::cout << sample->getIngredient() << std::endl; std::cout << "Cost: " << sample->getCost() << std::endl; }
The output of this program is given below:
Whip Milk Coffee
Cost: 2.2
// Class to be decorated function Coffee() { this.cost = function() { return 1; }; } // Decorator A function Milk(coffee) { this.cost = function() { return coffee.cost() + 0.5; }; } // Decorator B function Whip(coffee) { this.cost = function() { return coffee.cost() + 0.7; }; } // Decorator C function Sprinkles(coffee) { this.cost = function() { return coffee.cost() + 0.2; }; } // Here's one way of using it var coffee = new Milk(new Whip(new Sprinkles(new Coffee()))); alert( coffee.cost() ); // Here's another var coffee = new Coffee(); coffee = new Sprinkles(coffee); coffee = new Whip(coffee); coffee = new Milk(coffee); alert(coffee.cost());
Window System
# the Window base class class Window(object): def draw(self, device): device.append('flat window') def info(self): pass # The decorator pattern approch class WindowDecorator: def __init__(self, w): self.window = w def draw(self, device): self.window.draw(device) def info(self): self.window.info() class BorderDecorator(WindowDecorator): def draw(self, device): self.window.draw(device) device.append('borders') class ScrollDecorator(WindowDecorator): def draw(self, device): self.window.draw(device) device.append('scroll bars') def test_deco(): # The way of using the decorator classes w = ScrollDecorator(BorderDecorator(Window())) dev = [] w.draw(dev) print dev test_deco()
Difference between subclass method and decorator pattern
# The subclass approch class BorderedWindow(Window): def draw(self, device): super(BorderedWindow, self).draw(device) device.append('borders') class ScrolledWindow(Window): def draw(self, device): super(ScrolledWindow, self).draw(device) device.append('scroll bars') # combine the functionalities using multiple inheritance. class MyWindow(ScrolledWindow, BorderedWindow, Window): pass def test_muli(): w = MyWindow() dev = [] w.draw(dev) print dev def test_muli2(): # note that python can create a class on the fly. MyWindow = type('MyWindow', (ScrolledWindow, BorderedWindow, Window), {}) w = MyWindow() dev = [] w.draw(dev) print dev test_muli() test_muli2()
Dynamic languages
The decorator pattern can also be implemented in dynamic languages either with interfaces or with traditional OOP inheritance.