Wednesday, 4 February 2015

The Decorator pattern design pattern

Background

This pattern employes a very important OO design principle -
  • Classes should be open for extension but closed for modification.
This design pattern allows new functionality to be added to an existing Object without modifying it's structure.  Also this is the 1st structural design pattern that we are taking a look at. If you remember there are 3 types of design patterns -

  1. Creational
  2. Structural
  3. Behavioral

Lets take an example to understand this better. Lets say you have opened a Pizza shop and you offer various types of Pizzas.



You then created classes for each type of Pizza. Now that you are very well versed with following design principle -
  • Code for interface and not implementation
you would probably create an interface Pizza and then make concrete class like PaneerPizza, ChickenPizza by implementing Pizza interface. But now lets say your business grows and you offer various types of toppings like extra cheese or mushrooms or spicy chicken or corn. For this you will have to now either create new concrete Pizzas or alter existing code. But this is not practical as every time you decide to offer or remove a new topping you code will change (Think of each Pizza having a getCost() method which will differ with toppings). This is where Decorator pattern comes to the rescue.


Generic Class Diagram



Source : Wiki


To the Code....

Lets first create an interface - Pizza

public interface Pizza {
    int getCost();
}

Now lets create concrete classes for Pizza - 

public class PaneerPizza implements Pizza {

    @Override
    public int getCost() {
        return 3;
    }

}

and

public class ChickenPizza implements Pizza {

    @Override
    public int getCost() {
        return 5;
    }

}

And now lets create decorator class. Remember decorator class will implement Component and will also have an instance of Component in it. Also the operation method which is this case is getCost() will be abstract.

public abstract class PizzaDecorator implements Pizza {

    

    Pizza pizza;



    public abstract int getCost();



}

and now lets create concrete classes for Toppings which will extend PizzaDecorato.

public class MushroomToppings extends PizzaDecorator {

    public MushroomToppings(Pizza pizza) {
        this.pizza = pizza;
    }
    
    @Override
    public int getCost() {
        return 2 + pizza.getCost();
    }

}

and

public class CornTopping extends PizzaDecorator {

    public CornTopping(Pizza pizza) {
        this.pizza = pizza;
    }
    
    @Override
    public int getCost() {
        return 1 + pizza.getCost();
    }

}

and  that's all with the model classes. Now lets simulate it and see,

/**
 * @author athakur
 */
public class PizzaSimulation {
    
    public static void main(String args[])
    {
        Pizza pizza = new ChickenPizza();
        System.out.println("Pizza cost : " + pizza.getCost());
        
        Pizza pizzaWithCornToppings = new CornTopping(pizza);
        System.out.println("Pizza cost : " + pizzaWithCornToppings.getCost());
        
        Pizza pizzaWithCornAndMushroomToppings = new MushroomToppings(pizzaWithCornToppings);
        System.out.println("Pizza cost : " + pizzaWithCornAndMushroomToppings.getCost());
        
    }
}



And the output is -

Pizza cost : 5
Pizza cost : 6
Pizza cost : 8


Now go back to each Pizza and Topping class and verify the output. It's pretty straight forward. See how we could dynamically add toppings and compute total cost without actually modifying the original class? That the power of Adapter patter.


NOTE : In decorator pattern decorator implements the component and also composes the component.
NOTE : In adapter pattern adapter implements the target and composes the adaptee.

Class Diagram for Above Pizza Store Code

Related Links


Summary

  • Decorator pattern does not alter any interface. It simply adds responsibility.
  • Adapter pattern Converts one interface (Adaptee) to another (Target) for compatibility.
  • Facade pattern simplifies interface for complex subsystem subclasses.
t> UA-39527780-1 back to top