Decorator Design Pattern


Intent

It is belongs to structural design patterns catalog.
Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.

Also known as

Wrapper

Explanation

Real world example

There is an angry troll living in the nearby hills. Usually it goes bare handed but sometimes it has a weapon. To arm the troll it's not necessary to create a new troll but to decorate it dynamically with a suitable weapon.

In plain words

Decorator pattern lets you dynamically change the behavior of an object at run time by wrapping them in an object of a decorator class.

Wikipedia says

In object-oriented programming, the decorator pattern is a design pattern that allows behavior to be added to an individual object, either statically or dynamically, without affecting the behavior of other objects from the same class. The decorator pattern is often useful for adhering to the Single Responsibility Principle, as it allows functionality to be divided between classes with unique areas of concern.

Structure


Participants


1. Component

  • defines the interface for objects that can have responsibilities added to them dynamically.

2. ConcreteComponent

  • defines an object to which additional responsibilities can be attached.

3. Decorator
  • maintains a reference to a Component object and defines an interface that conforms to Component's interface.
4. ConcreteDecorator
  • adds responsibilities to the component.

Collaborations

  • Decorator forwards requests to its Component object. It may optionally perform additional operations before and after forwarding the request. 

Source Code (First Example)

Let's take the troll example. First of all we have a simple troll implementing the troll interface
public interface Troll {
  void attack();
  int getAttackPower();
  void fleeBattle();
}

public class SimpleTroll implements Troll {

  private static final Logger LOGGER = LoggerFactory.getLogger(SimpleTroll.class);

  @Override
  public void attack() {
    LOGGER.info("The troll tries to grab you!");
  }

  @Override
  public int getAttackPower() {
    return 10;
  }

  @Override
  public void fleeBattle() {
    LOGGER.info("The troll shrieks in horror and runs away!");
  }
}
Next we want to add club for the troll. We can do it dynamically by using a decorator
public class ClubbedTroll implements Troll {

  private static final Logger LOGGER = LoggerFactory.getLogger(ClubbedTroll.class);

  private Troll decorated;

  public ClubbedTroll(Troll decorated) {
    this.decorated = decorated;
  }

  @Override
  public void attack() {
    decorated.attack();
    LOGGER.info("The troll swings at you with a club!");
  }

  @Override
  public int getAttackPower() {
    return decorated.getAttackPower() + 10;
  }

  @Override
  public void fleeBattle() {
    decorated.fleeBattle();
  }
}
Here's the troll in action
// simple troll
Troll troll = new SimpleTroll();
troll.attack(); // The troll tries to grab you!
troll.fleeBattle(); // The troll shrieks in horror and runs away!

// change the behavior of the simple troll by adding a decorator
troll = new ClubbedTroll(troll);
troll.attack(); // The troll tries to grab you! The troll swings at you with a club!
troll.fleeBattle(); // The troll shrieks in horror and runs away!

Source Code (Second Example)

Step 1:Create a Food interface.
public interface Food {  
    public String prepareFood();  
    public double foodPrice();  
}// End of the Food interface.
Step 2: Create a VegFood class that will implements the Food interface and override its all methods.
public class VegFood implements Food {  
    public String prepareFood(){  
         return "Veg Food";  
    }  
  
        public double foodPrice(){  
        return 50.0;  
    }  
}  
Step 3:Create a FoodDecorator abstract class that will implements the Food interface and override it's all methods and it has the ability to decorate some more foods.
public abstract class FoodDecorator implements Food{  
    private Food newFood;  
    public FoodDecorator(Food newFood)  {  
        this.newFood=newFood;  
    }  
    @Override  
    public String prepareFood(){  
        return newFood.prepareFood();   
    }  
    public double foodPrice(){  
        return newFood.foodPrice();  
    }  
}  
Step 4:Create a NonVegFood concrete class that will extend the FoodDecorator class and override it's all methods.
public class NonVegFood extends FoodDecorator{    
    public NonVegFood(Food newFood) {  
        super(newFood);  
    }  
    public String prepareFood(){  
        return super.prepareFood() +" With Roasted Chiken and Chiken Curry  ";   
    }  
    public double foodPrice()   {  
        return super.foodPrice()+150.0;  
    }  
}  
Step 5:Create a ChineeseFood concrete class that will extend the FoodDecorator class and override it's all methods.
public class ChineeseFood extends FoodDecorator{  
  public ChineeseFood(Food newFood)    {  
        super(newFood);  
  }  
    public String prepareFood(){  
        return super.prepareFood() +" With Fried Rice and Manchurian  ";   
    }  
    public double foodPrice()   {  
        return super.foodPrice()+65.0;  
        }  
}  
Step 6:Create a DecoratorPatternCustomer class that will use Food interface to use which type of food customer wants means (Decorates).
import java.io.BufferedReader;  
import java.io.IOException;  
import java.io.InputStreamReader;  
public class DecoratorPatternCustomer {  
    private static int  choice;  
    public static void main(String args[]) throws NumberFormatException, IOException    {  
       do{        
        System.out.print("========= Food Menu ============ \n");  
        System.out.print("            1. Vegetarian Food.   \n");  
        System.out.print("            2. Non-Vegetarian Food.\n");  
        System.out.print("            3. Chineese Food.         \n");  
        System.out.print("            4. Exit                        \n");  
        System.out.print("Enter your choice: ");  
        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));   
        choice=Integer.parseInt(br.readLine());  
        switch (choice) {  
        case 1:{   
                 VegFood vf=new VegFood();  
              System.out.println(vf.prepareFood());  
              System.out.println( vf.foodPrice());  
            }  
            break;  
              
                case 2:{  
                Food f1=new NonVegFood((Food) new VegFood());  
                    System.out.println(f1.prepareFood());  
                System.out.println( f1.foodPrice());  
        }  
            break;    
     case 3:{  
             Food f2=new ChineeseFood((Food) new VegFood());  
                     System.out.println(f2.prepareFood());  
                    System.out.println( f2.foodPrice());  
              }  
            break;    
              
         default:{    
            System.out.println("Other than these no food available");  
        }         
    return;  
     }//end of switch  
          
}while(choice!=4);  
    }  
} 
Output : 
========= Food Menu ============  
            1. Vegetarian Food.  
            2. Non-Vegetarian Food.  
            3. Chineese Food.  
            4. Exit  
Enter your choice: 1  
Veg Food  
50.0  
========= Food Menu ============  
            1. Vegetarian Food.  
            2. Non-Vegetarian Food.  
            3. Chineese Food.  
            4. Exit  
Enter your choice: 2  
Veg Food With Roasted Chiken and Chiken Curry  
200.0  
========= Food Menu ============  
            1. Vegetarian Food.  
            2. Non-Vegetarian Food.  
            3. Chineese Food.  
            4. Exit  
Enter your choice: 3  
Veg Food With Fried Rice and Manchurian  
115.0  
========= Food Menu ============  
            1. Vegetarian Food.  
            2. Non-Vegetarian Food.  
            3. Chineese Food.  
            4. Exit  
Enter your choice: 4  
Other than these no food available 

Applicability

Use Decorator
  • To add responsibilities to individual objects dynamically and transparently, that is, without affecting other objects
  • For responsibilities that can be withdrawn
  • When extension by subclassing is impractical. Sometimes a large number of independent extensions are possible and would produce an explosion of subclasses to support every combination. Or a class definition may be hidden or otherwise unavailable for subclassing

Real world examples


Credits