Decorator design pattern in Java

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.

What is the Decorator design pattern?

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.

What problem does the Decorator design pattern solve?

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).

Example of Decorator implementation in Java

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 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.
Output from the Decorator design pattern example.

Conclusion

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.

About the author

Jozef Wagner

Java Developer Senior

Viac ako 10 rokov programujem v Jave, momentálne pracujem v msg life Slovakia ako Java programátor senior a pomáham zákazníkom implementovať ich požiadavky do poistného softvéru Life Factory. Vo voľnom čase si rád oddýchnem v lese, prípadne si zahrám nejakú dobrú počítačovú hru.

Let us know about you