Design patterns Java: Decorator

Today we will take a look at another Java design pattern from the structural patterns category – Decorator. Design patterns in this category deal with class structure such as inheritance and composition.

What is the Decorator design pattern?

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 behavior 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?

It 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 behavior 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 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 Ice Cream 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 icing to ice cream. This class calls the vyrobZmrzlinu() method over the ice cream reference and coats it with chocolate frosting.

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 topping(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 behavior 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 behavior at runtime is a fundamental advantage of the Decorator design pattern.
Output from the Decorator design pattern example.

Summary

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 programmer, check out our company benefits and respond to our latest job postings.

About the author

Jozef Wagner

Java Developer Senior

I have been programming in Java for more than 10 years, currently I am working in msg life Slovakia as a senior Java programmer and I help customers to implement their requirements into Life Factory insurance software. In my free time I like to relax in the mountains or play a good computer game.

Let us know about you