Saturday, January 30, 2016

Decorator Design Pattern - Decorating Objects

You may also like to see:

Decorator Pattern is a design Pattern that allows to dynamically add behavior to an existing individual object without making any code changes to the underlying classes. It is a flexible replacement to sub-classing for extending functionality to an object.

If you think inheritance is everything than decorator pattern helps you to learn the power of extension at run-time instead of compile-time. Let's try to learn and implement decorator pattern on a problem.


Decorator Pattern in Pizza Corner Problem

Pizza Corner growing restaurant chain around. Because they have grown, they’re trying to update their ordering systems to match their Pizza offerings. While starting their business they created their classes like this.

Pizza Corner order system classes
Pizza Corner order system's classes design

Other than regular Pizza, you can ask for several pizza topping options like Bacon, Black olives, Chicken, Extra cheese, Mushrooms, Onions, Sausage and much more. Pizza Corner charges for each of these topping, so they want to add these into their system.


First Implementation

First implementation of adding new abilities in system team created a child class for each pizza with each topping option. So first implementation looks like.

If your first thought was also to use inheritance for this problem then you are incorrect. What if Pizza Corner started offering more toppings or added new pizza type you have to add all combination of classes in a system which will become hard to manage.

Alternate Solution

Developer team realize that this subclassed solution is not going to work and its actually a bad design so they started redesigning the system. Now they created instance variables for each topping option in the base class. So each child class can set options which it required.

Pizza Corner System with Instance variable

This solution reduced the size of classes but there is another problem if you have noticed now all the classes has all the topping options so Vegie can also have chicken topping which should not be allowed. Additionally, this design also violate the basic design principle of "Classes should be open
for an extension, but closed for modification." As whenever a new topping option will be introduced we need to modify our base class which is not correct.

Decorator Pattern


As we have seen different approaches for our problem at Pizza Corner which have not work out very well. Let's implement Decorator Pattern in this problem.


For decorator pattern we take our pizza type object and than decorate it with different toppings. Lets say we got an order for BBQChicken with Onions, ExtraCheese and Mushrooms.

  1. First take a BBQChicken object
  2. Decorate it with Onions
  3. Decorate it with ExtraCheese
  4. Decorate it with Mushrooms
  5. Call the cost() method and delegation will add cost of all topping and pizza

Pizza Corner System - Decorator Pattern
Decorator Pattern - Decorating Objects

Here are some key points for decorator pattern implementations:


  • Decorators object should have the same base type as the objects they are decorating.
  • We can use more than one decorators to wrap an object.
  • Given that the decorator has the same supertype as the object it decorates, we can pass around a decorated object in place of the original (wrapped) object.
  • The decorator object apply its own behavior either after or/and before delegating to the object it decorates.
  • We can decorate objects dynamically at runtime with as many decorator as we required.

Code Implementation

Here is a diagram which shows the design of decorator pattern. We have to implement this design for our system. I am going to implement it in C# language.

Decorator Pattern
Decorator Pattern - Image from Head First Design Pattern

Our base class of Pizza which will be inherited by all pizza types and toppings.

public abstract class Pizza 
{
  String description = “Unknown Pizza”;
  public String getDescription() 
  {
    return description;
  }

  public abstract double cost();
}

Here are our concrete components classes for each pizza type. Each concrete components will set is own definition for cost method and set description.

public class BBQChicken : Pizza
{
  public BBQChicken() 
  {
    description = “BBQ Chicken”; 
  }
  public double cost() 
  {
    return 800; //rupees
  }
}

public class HotChickenWings: Pizza
{
  public HotChickenWings () 
  {
    description = “Hot Chicken Wings”;
  }
  public double cost() 
  {
    return 750; //rupees
  }
}
public class Vegie : Pizza
{
  public Vegie() 
  {
    description = “Vegetable Pizza”; 
  }
  public double cost() 
  {
    return 650; //rupees
  }
}

Let implement topping decorator abstract class:

public abstract class ToppingDecorator : Pizza 
{
  public abstract String getDescription();
}

We have implemented our base topping decorator class, lets implement decorator class.

public class Onions : ToppingDecorator // ToppingDecorator inherit Pizza 
{
  Pizza pizza;
  public Onions(Pizza pizza) 
  {
    this.pizza = pizza;
  }
  public String getDescription() 
  {
    return pizza.getDescription() + “, Onions”;
  }
  public double cost() 
  {
    return 120 + pizza.cost();
  }
}
public class ExtraCheese : ToppingDecorator // ToppingDecorator inherit Pizza 
{
  Pizza pizza;
  public ExtraCheese(Pizza pizza) 
  {
    this.pizza = pizza;
  }
  public String getDescription() 
  {
    return pizza.getDescription() + “, ExtraCheese”;
  }
  public double cost() 
  {
    return 160 + pizza.cost();  
  }
}
// similarly implement all toppings

Serve Some Pizzas


As we have implemented decorator pattern for Pizza Corner lets serve some order of pizza and see how its going to work.

public class PizzaCorner
{
  public static void Main(String[] args)
  {

    Pizza pizza = new HotChickenWings();
    System.Console.WriteLine(pizza.getDescription() + “ Rs.” +pizza.cost());

    Pizza pizza2 = new BBQChicken();
    pizza2 = new Onions(pizza2);
    pizza2 = new ExtraCheese(pizza2);
    pizza2= new Mushrooms(pizza2);
    // cost triggering order is Mushrooms, ExtraCheese, Onions, BBQChicken
    System.Console.WriteLine(pizza2.getDescription()+ “ Rs” +pizza2.cost());
  }
}

Decorator Pattern in JavaScript

Let's implement the decorator pattern in JavaScript. Let's say a Cell Phone company implementing their system. They create Cell Phone with different features e.g, Camera, Wifi, 3G, Bluetooth etc each of these features has some cost. They want to implement their system in a way they don't need to change the whole system when they introduce a new phone model with some feature. Here we going to use decorator pattern in which we take base Cell Phone object and decorate it with Feature decorator.

//object we're going to decorate
function CellPhone() {
    this.cost = function () { return 397; };
    this.screenSize = function () { return 5; };
}
/*Decorator 1*/
function Camera(cellPhone) {
    var price = cellPhone.cost();
    cellPhone.cost = function() {
        return price + 45;
    }
}
 /*Decorator 2*/
function Wifi(cellPhone){
   //get cell phone current price
   var price = cellPhone.cost();
    
   //update cell phone cost() function
   //and add feature price to current price
   cellPhone.cost = function(){
     return  price + 90;
  };
}

 /*Decorator 3*/
function threeG(cellPhone){
   var price = cellPhone.cost();
   cellPhone.cost = function(){
     return  price + 90;
  };
}

/*Decorator 4*/
function Bluetooth(cellPhone){
   var price = cellPhone.cost();
   cellPhone.cost = function(){
     return  price + 50;
  };
}

Company introduced a new model with Camera, Wifi and bluetooth. Lets see how to decorate the object:

var newModelCellPhone = new CellPhone();
Camera(newModelCellPhone);
Wifi(newModelCellPhone);
Bluetooth(newModelCellPhone);
console.log(newModelCellPhone.cost());
console.log(newModelCellPhone.screenSize());

Conclusion

We have implemented Decorator Design Pattern which add behavior to an existing object without making any code changes to the underlying classes as you notice its follow the design principle of "Open for an extension, closed for the modification" Now if Pizza Corner add any new Topping serving the need to extend the system by implementing new decorator class and no need to existing system. If you have any question or feedback please post in comments.

You may also like to see:

No comments:

Post a Comment

Life insurance policy, bank loans, software, microsoft, facebook,mortgage,policy,