Decorator

From Wikibooks, open books for an open world
Jump to: navigation, search

Composite Computer Science Design Patterns
Decorator
Facade

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 the decorator 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.

using System;
 
// Extension methods must be parts of static classes.
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)
    {
        // Return the opposite of the target.
        return !target;
    }
}
Implementation in C++

Coffee making scenario

# include <iostream>
# include <string>
// The abstract coffee class
class Coffee
{
public:
    virtual double getCost() = 0;
    virtual std::string getIngredient() = 0;
    virtual ~Coffee() {}
};
// 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){}
    ~CoffeeDecorator()  {
        delete &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;
    delete sample;
}

The output of this program is given below:

Whip Milk Coffee
Cost: 2.2

Implementation in JavaScript
// 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());
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 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

Implementation in Python

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.

External links


Clipboard

To do:
Add more illustrations.


Composite Computer Science Design Patterns
Decorator
Facade


You have questions about this page?
Ask it here:


Create a new page on this book: