Decorator design pattern in java with examples. The decorator design pattern is one of the classic design patterns used to overcome the recurring design problems in object-oriented software. It allows us to add functionality to an object dynamically. By dynamically, we mean modifying the behaviour of an object at run time. Although we have used the term behaviour, but decorator design pattern falls in the category of structural design pattern, because it has something to do with the way we structure our classes. There are a few bumpy areas in using inheritance to which the decorator design pattern provides an elegant solution.
Decorator design pattern in java with examples
This article will first describe what decorator design pattern is, why to use it and then we will use a simple decorator design pattern java example to demonstrate its significance.
What is decorator design pattern in java?
Decorator design pattern in java proposes a way to customise our objects dynamically during run time, instead of us having to write code to do that during compile time. You can think of it as an alternative to sub classing. When we subclass a parent class, there might be multiple combination of classes resulting in a class explosion. As you can see in the image below, the beverage class is being subclassed by multiple classes to add their own blend to it. Although it seems to follow the Single Responsibility Principle, it quite does not seem an elegant solution and obviously is hard to maintain.
Fig: Decorator Design Pattern – Class Explosion
Why to use decorator design pattern?
The snippet you saw, certainly is mind blowing. What if there was a way to reduce the number of subclasses we create for every variation of an object? That would be a lot better solution and that is what the decorator design pattern has to offer. The approach is to make objects acquire responsibilities (feature, variation, combination, additional functionality) at run time. The concept of composition comes into play when doing this. Composition is pretty different from inheritance, you see. Our decorator design pattern example in java will make it more clear, composition refers to containing instances of other classes that implement desired functionality. Inheritance is more of prototyping a parent and based on that implementing the child classes. Composition describes a “has- a” relationship while inheritance describes an “is-a” relationship between objects.
Read: "Design Patterns: A quick guide to Observer pattern in Java"
Although inheritance is a foundational pillar of Object-Oriented Programming, completely relying on it for structuring classes will lead to class explosion, as we saw earlier. Therefore, we need design patterns, and specific ones for specific situations to handle messy and repetitive code patterns. And in our case, decorator design pattern comes in handy. Let us see a code example.
Decorator design pattern in java example
Suppose we have a pizza store that allows customers to buy a few types of pizzas – Thin crust pizza, Thick crust pizza and allow them to add toppings as well. Our task is to design a system that allows customers to get the description and cost of a particular type of pizza. Now, the catch here is they can demand any type of pizza, say thin crust pizza with cheese and olives. How would you do it?
The first thing that would come in your mind would be to create a Pizza class that could be then sub classed by thin crust pizza and thick crust pizza classes. In order to create thin crust pizza with cheese you would then create thin crust pizza with cheese class, also if there is a cheese thin crust pizza with olives, you will have to create that class as well. However now, you might have started to get the picture of class explosion in this case. Our class diagram might look like this.
Fig: Decorator Design Pattern in java – Class Explosion Pizza Example
Although, the class diagram seems neat, if we had more types or toppings, it would be the same as the one we showed earlier.
How can we solve this using the decorator design pattern? Lets see..
-
Firstly, we will define a component (Pizza Class) which will then be extended by concrete components (Thin Crust and Thick Crust Pizzas).
-
Then we will define a decorator that will extend the main component.
-
The decorator then will be used to extend the state of the main component by adding new functionalities to it.
Did that make sense? If no, then follow along…
1. Abstract Pizza Class (The main component)
Fig- decorator design pattern in java example : Main Component
This Pizza class will be used as the base to create other types of pizza. This is the main component of our system.
2. Concrete components
Fig- decorator design pattern in java example: Concrete Component (Thin crust Pizza)
Fig- decorator design pattern in java example: Concrete Component (Thick Crust Pizza)
The concrete components are the ones that will have dynamically added behaviour at run time. For example, “Thin crust pizza with, tomato sauce, cheese, olives”.
3. Decorators
Fig- decorator design pattern in java example: Topping Decorator abstract class or interface
As you can see the decorator implements the same abstract class (Pizza) that they will decorate. This is where the real game begins.
4. Implementing decorator objects
Fig- decorator design pattern in java example: Cheese Decorator
Fig- decorator design pattern in java example: Olives Decorator
As you can see in both the implementations, the decorators have reference to the instance variable that they will decorate, in this case it is Pizza. It can be any type of pizza, all it should be is of the type pizza. Additionally, they have their own extension of description and cost.
Read: "Top 5 Best Free Java Development IDE in 2020"
See them in action:
This is where everything wraps up, pizza has instance of ThincrustPizza() that has its own cost and description. The next line has “composed” Cheese and ThincrustPizza objects, Cheese class acts as a decorator for the Thincrust Pizza , so now we have ThincrustPizza with Cheese. Same applies to greekPizza. At the end ThinCrustPizza, Cheese and Olives have been composed. As mentioned earlier, “composition refers to containing instances of other classes that implement desired functionality”.In our case greekPizza has contained the reference of cheesePizza that has its own functionality, also cheesePizza has contained the reference of pizza. Thus, creating a “Thin crust Pizza with tomato sauce, cheese , olives”. This is how decorators work.
When to use decorator design pattern and what was the point of doing all these steps?
-
Decorator pattern solved the class explosion problem. We did not create subclasses for every type of pizza.
-
We can easily extend code without modifying other classes. Say, if we want Onion Pizzas with olives, we just need to extend the Topping decorator with Onion Pizza class and compose them like we did in the above example.
-
We can wrap the components with any number of decorators i.e. if we want onion pizza with olives and cheese, we simply need additional wrapping and not another subclass with this feature.
Read: "Factory Design Patterns in Java"
Are there any caveats to decorator design pattern in java?
Of course, there is, introducing decorators increases complexity of the code needed to instantiate the component. Also, it is just not about instantiation, there may be lots of wrapped objects that need to be instantiated at run time as well. The other caveat is that decorators are hard to implement in a way that does not affect the behaviour depending on how the decorators are stacked. But it surely solves an unnecessary class inheritance pattern.
If you have come this far, you surely need to know one thing, java is flooded with decorator design pattern and one such example is below. You can now guess that the BufferedInputStream is decorating the fin object.
Read: "Singleton Design Pattern – A thoughtful dive into Object Structures and Creation"
Finally, we would like to conclude this blog by saying – use decorators when it is not possible to extend an object’s behaviour using inheritance in a neat way. By neat we mean, code cleanliness and complexity.