
Project Technical Lead
Today we will take a look at another Java design pattern from the structural patterns category – Decorator. Java design patterns in this category deal with class structure such as inheritance and composition.
Decorator is a design pattern that is used to dynamically add (and remove) new responsibilities or features to existing objects without affecting the behaviour of other objects of the same class. Adding new functionality is also called decorating, hence the name of this design pattern.
Decorator solves the problem of changing instances of classes without the need to create additional derived classes, because it attaches the new functionality to the object dynamically. Using this pattern can be much more efficient than creating subclasses because the behaviour of an object can be extended without defining an entirely new object. It is mainly used when we need to extend an object (Decorator does not extend the class).
We will now write a program that demonstrates the use of the Decorator design pattern to create decorated ice creams (= zmrzlina in Slovak) with various extra ingredients.
The IZmrzlina interface defines a vyrobZmrzlinu() method that will be implemented by the various ice creams.
IZmrzlina.java
package designpatterns;
public interface IZmrzlina {
public String vyrobZmrzlinu();
}
The Zmrzlina class implements the IZmrzlina interface and provides a basic implementation of ice cream.
Zmrzlina.java
package designpatterns;
public class Zmrzlina implements IZmrzlina {
@Override
public String vyrobZmrzlinu() {
return "Zmrzlina";
}
}
The ZmrzlinaDecorator abstract class implements the IZmrzlina interface and contains a reference to the IZmrzlina instance to be decorated. This class returns the result of the vyrobZmrzlinu() method of the decorated ice cream.
ZmrzlinaDecorator.java
package designpatterns;
abstract class ZmrzlinaDecorator implements IZmrzlina {
protected IZmrzlina upravenaZmrzlina;
public ZmrzlinaDecorator(IZmrzlina upravenaZmrzlina) {
this.upravenaZmrzlina = upravenaZmrzlina;
}
@Override
public String vyrobZmrzlinu() {
return upravenaZmrzlina.vyrobZmrzlinu();
}
}
The CokoladaDecorator class is a particular decorator that adds chocolate syrup to ice cream. This class calls the vyrobZmrzlinu() method over the ice cream reference and coats it with chocolate syrup.
CokoladaDecorator.java
package designpatterns;
public class CokoladaDecorator extends ZmrzlinaDecorator {
public CokoladaDecorator(IZmrzlina upravenaZmrzlina) {
super(upravenaZmrzlina);
}
public String vyrobZmrzlinu() {
return upravenaZmrzlina.vyrobZmrzlinu() + pridajCokoladu();
}
private String pridajCokoladu() {
return ", poliata cokoladovou polevou";
}
}
The ArasidyDecorator class is another particular decorator that adds crushed peanuts to ice cream. Similar to the CokoladaDecorator,, this class calls the vyrobZmrzlinu() method over an ice cream reference and sprinkles it with crushed peanuts.
ArasidyDecorator.java
package designpatterns;
public class ArasidyDecorator extends ZmrzlinaDecorator {
public ArasidyDecorator(IZmrzlina upravenaZmrzlina) {
super(upravenaZmrzlina);
}
public String vyrobZmrzlinu() {
return upravenaZmrzlina.vyrobZmrzlinu() + pridajArasidy();
}
private String pridajArasidy() {
return ", posypana drvenymi arasidami";
}
}
In the main() method, we create a concrete ice cream object with a decorating sequence. Starting with the original ice cream (Zmrzlina), add first the chocolate syrup (CokoladaDecorator) and then the crushed peanuts (ArasidyDecorator).
Main.java
import designpatterns.*;
public class Main {
public static void main(String[] args) {
IZmrzlina zmrzlina = new ArasidyDecorator(new CokoladaDecorator(new Zmrzlina()));
System.out.println(zmrzlina.vyrobZmrzlinu());
}
}
The decorators can be combined in any order. This flexibility and dynamic change of instance behaviour at runtime is a fundamental advantage of the Decorator design pattern.
The Decorator design pattern is used when we need to add dynamic responsibility to an interface by wrapping the original code. If we used simple inheritance to add new functionality, all objects would inherit this functionality, even those that don’t need it. We can avoid this by using this pattern.
We have prepared the files with the above example in the form of code that you can run directly in Java. Download the Java Decorator code here.
Do you know the Java programming language? If you’re looking for a job as a Java developer, check out our employee benefits and respond to our latest job offers.