Let’s suppose; you go to a supermarket to get an ice cream cone. You are asking for a cone ice-cream. Here, the cone is the base object. If a customer asks for vanilla flavor, the vendor will add vanilla flavor in that base object. Adding the demanded flavor, the base object is converted into a customer object based on user request.
This means the base object is added with additional responsibility which is a vanilla flavor in this case. If another customer wants a chocolate flavor, the vendor will add the chocolate flavor ice cream as the additional responsibility at the run time.
If we take a plain pizza as an example and a customer orders for chicken pizza, so a chef will add the additional responsibility on the plain pizza which is a chicken layer in this case.
So, when the object should be treated dynamically at the run time based on the criteria, then we should choose Decorator Design Pattern.
public interface Pizza { public void getPizza(); }
public class MushroomPizza implements Pizza { @Override public void getPizza() { System.out.print("Mushroom Pizza"); } }
public class ChickenPizza implements Pizza { @Override public void getPizza() { System.out.print("Chicken Pizza"); } }
public abstract class PizzaDecorator implements Pizza { protected Pizza pizza; public PizzaDecorator(Pizza pizza) { this.pizza=pizza; } @Override public void getPizza() { this.pizza.getPizza(); } }
public class PizzaWithToppings extends PizzaDecorator { public PizzaWithToppings(Pizza pizza) { super(pizza); } @Override public void getPizza() { pizza.getPizza(); addToppings(); } private void addToppings() { System.out.print(" with toppings"); } }