Decorator Pattern in Java
What is Decorator Pattern?
The Decorator pattern in Java is defined as it attaches additional responsibilities to an object dynamically. Decorator pattern provides a flexible alternative to subclassing for extending functionality.
A problem to understand the need to implement Decorator Pattern in Java
A Dominico’s pizza company has started to build an ordering system. Initially they started the ordering system with some 5 pizza variety, later they were expanding their pizza varieties, topping’s varieties, cheese levels to their ordering system. Hence their ordering system should behave unique to every customer’s choice. Every customer decorates their pizza with different toppings and cheese levels, so the cost varies for every toppings and cheese levels.
Challenges in Dominico’s Pizza ordering system
- Cost changes for every toppings, every type of crust which will force us to alter existing code
- We may have new pizza type to add, toppings and crust needs to be added to it. It shouldn’t make us to alter the existing code of other pizza’s, toppings and crust
- What if customer wants 2 double cheese to be added
Our GOAL in decorator pattern
Allow classes to be easily extended to incorporate new behavior without modifying existing code.
Classes should be open for extension, but closed for modification which we call as OPEN-CLOSED Principle
Note: Be careful when choosing the area of code that need to be extended; applying the open-closed principle EVERYWHERE, is wasteful and unnecessary, and can lead to complex, hard-to-understand code.
Overview of Steps for Implementing Decorator Pattern in Java for Dominico’s Pizza Shop
Create a super-type Pizza
Create decorators(toppings, crust, cheese) as type of Pizza(super-type)
Decorators have the same super-types as the object they decorate
You can use one or more decorators to wrap an object
Given 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 adds its own behavior either before and/or after delegating to the object it decorates to do the rest of the job
Objects can be decorated at any time, so we can decorate objects dynamically at run time with as many decorators we like
Let’s create a super-type Pizza which doesn’t need to change from Dominico’s Original design.
public abstract class Pizza { String description = "Pizza not found"; public String getDescription() { return description; } public abstract double cost(); }
Pizza is simple enough. Let’s implement the abstract class for the Toppings, Crust, Cheese (Decorator) as well:
public abstract class PizzaDecorator extends Pizza{ public abstract String getDescription(); }
Let’s implement some Pizza’s
public class VegPizza extends Pizza{ public VegPizza() { description = "Veg Pizza"; } @Override public double cost() { return 2.00; } }
public class NonVegPizza extends Pizza{ public NonVegPizza() { description = "Non Veg Pizza"; } @Override public double cost() { return 3.00; } }
Coding the decorators
public class VegToppings extends PizzaDecorator{ Pizza pizza; public VegToppings(Pizza pizza) { this.pizza = pizza; } @Override public String getDescription() { return pizza.getDescription()+" + Veg Toppings"; } public double cost() { return pizza.cost() + .30; } }
public class Cheese extends PizzaDecorator{ Pizza pizza; public Cheese(Pizza pizza) { this.pizza = pizza; } @Override public String getDescription() { return pizza.getDescription()+" + extra Cheese"; } public double cost() { return pizza.cost() + .20; } }
Serving the Pizza’s
public class DominicoPizzaShop { public static void main(String[] args) { Pizza pizza1 = new VegPizza(); System.out.println(pizza1.getDescription()+" - Cost $"+pizza1.cost()); Pizza pizza2 = new NonVegPizza(); pizza2 = new VegToppings(pizza2); pizza2 = new Cheese(pizza2); System.out.println(pizza2.getDescription()+" - Cost $"+pizza2.cost()); } }
Output
Veg Pizza - Cost $2.0 Non Veg Pizza + Veg Toppings + extra Cheese - Cost $3.5
Note: Better way of creating decorated objects can be made with Factory and Builder design pattern.
Real World Decorators: Java I/O
The large numbers of classes in the java.io package is made of decorator objects.
InputStream and OutputStream acts as the abstract components that we will wrap with decorators.
Downside of Decorator Pattern in Java
Java I/O points out one of the downsides of the decorator pattern: designs using this pattern often results in a large number of small classes that can be difficult to developers trying to use the Decorator-based API.
Conclusion
The decorator pattern involves a set of decorator classes that are used to wrap concrete components. Decorator classes mirror the type of the components they decorate.
Tags In
dhamodaranr
Design Patterns Tutorial
Check out other post related to Design Patterns