Decorator Pattern — What It Is And How To Use It

Last year, I was on a business trip to Mumbai, I was feeling hungry and wanted to order delicious and expensive food 😜. And I was craving for pizza so, ordered Dominos cheese burst pizza along with loads of extra toppings. Of course, it was just as mouth-watering as the above picture! Today, we’ll learn about the Decorator design pattern with an example of pizza. Suppose we are developing a billing software for the dominos. Customers can order their favorite pizza with extra toppings on it. We have different types of base, a variety of toppings that add up to the actual cost of the pizza. How can we build such kind of system such that it requires minimal modification yet we can add new functionality in the future? Before digging into the decorator pattern, let’s learn a design principle.

The open-closed principle — A system should be closed to modification and open for extension.

Let’s explore a little bit here. Let’s say, we have a Person class with firstName as a class variable.

class Person {
String firstName;
Person(String firstName){
this.firstName = firstName;
}
}

Now as a change in requirement, we might want to add lastName as a class variable. For this example, we can definitely do it; however, we can’t always do that. Because it might be possible that this person’s class might be used by multiple other teams. We may end up in a situation where a little change can break the existing code of the system. Hence, we should always stress on building systems that are closed for modification, but they should be open for extension, like adding new functionality on top of existing ones.

Okay, coming back to our pizza requirement, we have different types of pizza bases, different types of toppings how can we approach it. Here are a few methods.

We can now check if extra chess is present, then we can add the extra cost of cheese to the pizza. This is definitely a better strategy compared to the previous one! But wait, there are problems here.

This is not a good design as we are violating the open-closed principle.

The decorator pattern comes to the rescue! We’ll start with our pizza base class and add all the toppings at runtime. Here is the procedure of the decorator pattern.

Hence in this way, decorators (in our case toppings) can be added at the runtime with as many decorators as we like. Cool, now without wasting more time, let’s jump straight to the code!

Let’s add the Pizza class that will be abstract or base class for all of the other classes. It can be an interface too. We’ve also implemented a concrete pizza — FarmHouse.

Now let’s define our toppings. Code is pretty self-explanatory.

Let’s see how we can get our favorite pizza and calculate its cost. We have olive as the outermost object, followed by paneer and then base pizza at the center of the circle.

The getPrice() method will call the getPrice method defined in the Olive class. Followed by Paneer and Pizza.

Total Price = 40 (Price of extra olive) + 50 (Price of extra paneer) + Cost of base pizza.

Phew! That’s the end of the decorator pattern. No wait, let’s look at the definition of the pattern in the Head First Design Pattern book.

The decorator pattern attaches additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending the functionality.

Thank you for reading till now,

Happy Coding!

Let’s be an awesome developer together! I will be learning the tech and sharing it with the community.