Java Decorator Design Pattern Example
1. Introduction to Java Decorator Design Pattern
In this article we will discuss Java Decorator design pattern in detail. The decorator design pattern is often used to alter an object’s features at run time. Simultaneously, it would not impact other instances of the very same class, therefore the behaviour of each object is altered. The java Decorator design pattern is among the structural design patterns such as the Adapter pattern, the Bridge pattern and the Composite pattern. A structural design pattern is useful to structure our code better and reduce the redundancy in the code. Decorator design pattern precisely helps in doing that.
It’s being used to broaden or alter the behavior of’ an instance’ in the
decorator design pattern at runtime. Inheritance is used to increase’ class’ capabilities. In contrast to the inheritance, a class object can be chosen and its actions will be altered, so that other instances remain unchanged. Then we build a container over an object by expanding its behavior in the implementation of the decorator design pattern. Before or after the container would do its work and transfer the call to the instance.
In this article, we will begin by demonstrating the various different available design patterns and preceded by a real life instance of the decorator design pattern. This could help to understand precisely when you’re using the decorator design pattern. After that, we will learn how to program classes to implement the java decorator design pattern.
2. Types of Java Design Patterns
Design patterns are a recent innovation for software development. A prevalent and well-portrayed solution is a design pattern for a powerful software problem. A delicate use of design patterns results in increased programming repairs, as design patterns can be acknowledged by some as a perfect solution to a common issue and thus reduce the contour in the conduct of a particular piece of software. Design patterns comprise of design patterns such as creational, structural and behavioral design pattern.
2.1 Creational Pattern
Creational design patterns illustrate the instantiation process. This design pattern relies primarily on the composition and inheritance. They enable us to move from the tough programming of a specific set of behaviors to the logic of a smaller set of common behaviors which can be made up of much more complicated ones. This involves something more than a class to create objects with a particular behavior. There are five creational designs pattern: Abstract Factory, Prototype, Factory Method, Builder and Singleton.
2.2 Structural Pattern
Structural patterns contribute to the setup of larger systems in classes and objects. For the composition of interfaces or different implementations, inhertance in structural design patterns is used. For example, two or more classes of inheritance functionalities can be coupled into one class. This allows two or more class libraries to operate freely together. This enables structural object patterns to determine how to develop new objects. The mobility of the object’s composition allows us to change the structure during run-time.
2.3 Behavioral Pattern
The communication of objects is stated by a behavioral design pattern. It discusses how different objects and classes send one another information so that things are happening and how the relevant task steps are split into various objects. When creative patterns define a time and structural patterns define a hierarchy that is almost static, behavioral patterns define a mechanism or flow.
We will explore one of the structural design patterns below in this article-the Decorator design pattern. The decorator design pattern is a type of structural design pattern. This pattern generates a class of decorators that envelops the original class and provides extra features, keeping the fingerprint of class methods undamaged.
3. Decorator Design Pattern – Real life example
The Decorator appends extra obligations to an article powerfully. The adornments that are added to pine or fir trees are instances of Decorators. Lights, festoon, sweet sticks, glass adornments, and so on., can be added to a tree to give it a happy look. The decorations don’t change the tree itself which is unmistakable as a Christmas tree paying little mind to specific trimmings utilized. For instance of extra usefulness, the expansion of lights enables one to “light up” a Christmas tree.
Now we’ll look at a real world example of the Decorator design pattern in this part. The following scenario explains how the Decorator patterns help users to introduce new features to an existing object without changing its hierarchy. So the original class is not changed. Here we take an example of manufacturing of ice-creams and understand how it is identical to the Decorator design pattern to produce various different variety of ice-creams.
The following picture shows the basic steps of how decorator design pattern is used in manufacturing of different vareities of ice-creams. The Ice-cream shown in the image is only intended for the intent of knowledge and has no direct relationship with anyone.
In the above image we can see a vanilla ice-cream is made for the customer. And if the customer asked to add some fruit and nuts or add some flavour like chocolate, mango, strawberry etc. in the ice-cream. To add some extra toppings to the ice-cream here decorator design pattern comes into picture. We will create a ice-cream decorator and when the customer requirement changes the decorator class adds toppings to the ice-cream. Suppose, if customer asked to add fruit and nuts in the ice-cream then the decorator will add fruit and nut topping into the ice-cream. And, if the customer asked to add some extra flavour into the ice-cream such as chocolate or mango then the decorator will add the chocolate or mango flavour to the ice-cream. The decorator pattern basically adds or include additional features to the existing objects.
Decorator pattern enables a client to add new usefulness to a current article without changing its structure. This kind of design pattern goes under auxiliary pattern as this pattern goes about as a wrapper to existing class. This pattern makes a decorator class which wraps the first class and gives extra usefulness keeping class techniques signature unblemished.
4. Implementing Decorator Design Pattern
In this section, we will comprehend how the decorator design pattern operates by implementing it in java. We take an example of a famous south indian dish “dosa” and how the chef makes different vareities of dosa according to the customer requirement using dosa decorator.
Dosa.java
public interface Dosa { public String makeDosa(); }
In the above code, we have created an interface called dosa. In his interface we created a public method makeDosa() of string type.
PlainDosa.java
public class PlainDosa implements Dosa { @Override public String makeDosa() { return "Plain Dosa"; } }
In the above code we have created a class called PlainDosa which implements Dosa interface. Inside this class we implemented the makeDosa() method which we defined inside the Dosa interface.
DosaDecorator.java
abstract class DosaDecorator implements Dosa { protected Dosa dosa; public DosaDecorator( Dosa dosa ) { this.dosa = dosa; } public String makeDosa() { return dosa.makeDosa(); } }
In the above code, we have created a class called DosaDecorator which implements the Dosa interface. In this class we created a protected variable named dosa of type Dosa and implemented DosaDecorator() method with the parameter dosa. And then we created a method named makeDosa() inside this method we called makeDosa() method using the dosa variable.
MasalaDosa.java
public class MasalaDosa extends DosaDecorator { public MasalaDosa( Dosa dosa ) { super(dosa); } public String makeDosa() { return dosa.makeDosa() + addMasala(); } private String addMasala() { return ",Masala added"; } }
In the above code, we have created a class MasalaDosa which extends the DosaDecorator class. In this class we created a MasalaDosa() method with parameter dosa and inside this method we called super() method with parameter dosa. Then we created makeDosa() method inside this method we called makedosa() method using dosa variable and addMasala() method combined and returned. Finally, we created the addMasala() method which we called inside the makeDosa() method.
OnionDosa.java
public class OnionDosa extends DosaDecorator { public OnionDosa( Dosa dosa ) { super(dosa); } public String makeDosa() { return dosa.makeDosa() + addOnion(); } private String addOnion() { return ",Onion added"; } }
In the above code, we have created a class OnionDosa which extends the DosaDecorator class. In this class we created a OnionDosa() method with parameter dosa and inside this method we called super() method with parameter dosa. Then we created makeDosa() method inside this method we called makedosa() method using dosa variable and addOnion() method combined and returned. Finally, we created the addOnion() method which we called inside the makeDosa() method.
MysoreDosa.java
public class MysoreDosa extends DosaDecorator { public MysoreDosa( Dosa dosa ) { super(dosa); } public String makeDosa() { return dosa.makeDosa() + addMysoreMasala(); } private String addMysoreMasala() { return ",Mysore Masala added"; } }
In the above code, we have created a class MysoreDosa which extends the DosaDecorator class. In this class we created a MysoreDosa() method with parameter dosa and inside this method we called super() method with parameter dosa. Then we created makeDosa() method inside this method we called makedosa() method using dosa variable and addMysoreMasala() method combined and returned. Finally, we created the addMysoreMasala() method which we called inside the makeDosa() method.
DecoratorDesignPattern.java
public class DecoratorDesignPattern { public static void main( String args[] ) { PlainDosa plainDosaObj = new PlainDosa(); String plainDosa = plainDosaObj.makeDosa(); System.out.println(plainDosa); String onionDosa = new OnionDosa(plainDosaObj).makeDosa(); System.out.println("\n'" + onionDosa + "' using OnionDosa"); String masalaDosa = new MasalaDosa(plainDosaObj).makeDosa(); System.out.println("\n'" + masalaDosa + "' using MasalaDosa"); String mysoreDosa = new MysoreDosa(plainDosaObj).makeDosa(); System.out.println("\n'" + mysoreDosa + "' using MysoreDosa"); } }
In the above code, we have created a DecoratorDesignPattern class inside this we have created the main() method. In the main() method we created a PlainDosa object using PlainDosa class and we created a plainDosa variable and called the makeDosa() method. Then we created onionDosa variable of type string using OnionDosa class and makeDosa() method. Similarly, we have done for the Masala Dosa And Mysore Dosa.
'Plain Dosa,Onion added' using OnionDosa 'Plain Dosa,Masala added' using MasalaDosa 'Plain Dosa,Mysore Masala added' using MysoreDosa
In the output, Onion dosa is made using OnionDosa decorator and Masala Dosa is made using MasalaDosa decorator similarly Mysore Dosa is made using MysoreDosa decorator. All the three vareities of dosa is made form the Plain Dosa.
5. Benefits of Decorator Design Pattern
We explore several benefits of the decorator design pattern in this section. The following are the benefits of the decorator design pattern mentioned here:
- Decorator pattern offer a adaptable option to subclassification to broaden functionality.
- The decorator design pattern promotes the concept that classes that ought to be open for extension but closed down for amendment.
- The decorator pattern will be used to lengthen a certain object’s features during run time.
- The decorator design pattern is an alternative option to subclassification. Subclassing brings compile time actions and the switch impacts all instances of the primary class so decoration can give specific objects a new runtime behavior.
- Decorators enable behavior alter at run time instead of reverting to current code and making adjustments.
- Decorators are a great solution to permutation problems since a element can be wrapped with any number of decorators.
- It is adaptable than inheritance because inheritance adds responsibility at time of compilation, but the decorator pattern brings to runtime.
- Decorator design patterns are used mostly to extend single burden principles, as features is divided into classes with distinctive areas for improvement.
6. Conclusion
In conclusion, the decorator design pattern offers better stability than static inheritance. It improves the object’s modifiability because new classes are coded to make changes. The pattern of the decorator will be used when adaptively adding duties during run time. The Decorator design pattern fixes the issue whenever you want to append more duties during run time to an instance of an object. If inheritance is not relevant, the decorator design pattern jumps into the picture. The Decorator pattern is the heart and soul and is just a wrapper. The main purpose is to adaptively add actions to an object. These actions will in turn allow the child classes to inherit the actions and act on them. Thus, the decorator will be acting as an essence for the underlying code structure.
7. Download the Project
You can download the project files for the above example from the below link:
You can download the full source code of this example here: Java Decorator Design Pattern Example