The Decorator design pattern is a structural design pattern that allows you to add functionalities to an object at runtime.
Why Decorator ?
- If you need to attach additional responsibilities to an object dynamically.
- When sub-classing becomes impractical.
- You have a large number of possible functionalities and you need independent ways to add them to object.
Participants
- Component
- Concrete Component
- Decorator
- Concrete Decorator
Problems with Decorator
- Have to manage more objects, so there is an increased chance of coding errors
this is what i overheard at the Pizza store down the street…
Customer1: Get me a Veggie Pizza with lots of low-fat cheese, and don’t forget to top it with some spinach…
Customer2: For me, a Cheese pizza decorated with broccoli and red-hot sauce…and also double liquid mozzarella cheese…
Suppose the owner of this pizza shop approaches you to write an application for his business, after all you are an awesome OO Developer. Think….what classes you need to write ???
If you say, one for each kind of pizza, i would remind you of a large list of toppings the pizza shop offers…after all whats a pizza without toppings?? :p and also a customer can ask for double toppings …say double cheese toppings and single sauce…then in that case would you write
VeggiePizzaWithDoubleMozzarellaCheeseAndSauce
VeggiePizzaWithLowfatCheeseAndSpinach
CheesePizzaWithMozzarellaCheeseAndSauce and so on……
Its like writing a class for every permutation & combination of available pizza types and toppings.
Think….Think….Think….
In such cases, the decorator design pattern is what you need
Component – Pizza
public abstract class Pizza { String description; abstract String getDescription(); abstract int getCost(); //other useful methods }
Concrete Component – CheesePizza and VeggiePizza
public class CheesePizza extends Pizza { public CheesePizza() { description = "Cheese Pizza"; } @Override String getDescription() { return description; } @Override int getCost() { return 95; } }
public class VeggiePizza extends Pizza { public VeggiePizza() { description = "Veggie Pizza"; } @Override String getDescription() { return description; } @Override int getCost() { return 80; } }
Meet the Decorator
public abstract class PizzaTopping extends Pizza { @Override public abstract String getDescription(); }
The decorator extends the abstract Component – Pizza
You might be thinking…. isn’t decorator pattern used when we need to enhance an object’s functionality at runtime (using composition) rather than at compile-time (using inheritance) ???
The decorators should have the same type as the objects they are going to decorate. So here we’re using inheritance to achieve the type matching, not to get behavior.
and then the delicious Concrete Decorators…
All Concrete Decorators extend the abstract Decorator – PizzaTopping
I’ve shown only 3 of them – LowFatCheese, PepperSauce and Broccoli
public class LowFatCheese extends PizzaTopping { Pizza pizza; public LowFatCheese(Pizza pizza) { this.pizza = pizza; } @Override public String getDescription() { return pizza.getDescription() + " + Low-fat Cheese"; } @Override public int getCost() { return 30 + pizza.getCost(); } }
PepperSauce
public class PepperSauce extends PizzaTopping { Pizza pizza; public PepperSauce(Pizza pizza) { this.pizza = pizza; } @Override public String getDescription() { return pizza.getDescription() + " + Pepper Sauce"; } @Override public int getCost() { return 20 + pizza.getCost(); } }
Broccoli
public class Broccoli extends PizzaTopping { Pizza pizza; public Broccoli(Pizza pizza) { this.pizza = pizza; } @Override public String getDescription() { return pizza.getDescription() + " + Broccoli"; } @Override public int getCost() { return 20 + pizza.getCost(); } }
We compose a decorator with a component to add new behavior. Yes you are correct, it is because we are using object composition we get a whole lot more flexibility to top a pizza with any topping any number of times.
Its time to order some pizzas…
public class DecoratorTest { public static void main(String ar[]) { //A Veggie Pizza without any Toppings Pizza pizza1 = new VeggiePizza(); System.out.println(pizza1.getDescription() + " Rs. " + pizza1.getCost()); //A Veggie Pizza with Low-fat Cheese and Broccoli Pizza pizza2 = new LowFatCheese(new Broccoli(new VeggiePizza())); System.out.println(pizza2.getDescription() + " Rs. " + pizza2.getCost()); //A Cheese Pizza with Spinach and double Moozzarella cheese Pizza pizza3 = new Spinach(new MozzarellaCheese(new MozzarellaCheese(new CheesePizza()))); System.out.println(pizza3.getDescription() + " Rs. " + pizza3.getCost()); } }
Did you observe the chain that we created ?, linking one topping to other and finally with a plain pizza. you would be spared if you call this way of creating objects as Chaining. It is also called as Wrapping, as we wrap one object with another.
and finally the output
Veggie Pizza Rs. 80
Veggie Pizza + Broccoli + Low-fat Cheese Rs. 130
Cheese Pizza + Mozzarella Cheese + Mozzarella Cheese + Spinach Rs. 165
Happy Decorating 🙂