Design patterns are typical solutions to common problems in software design. Each pattern is like a blueprint that you can customize to solve a particular design problem in your code.

A decorator pattern allows users to add new functionality to an existing object without altering its structure. This design pattern comes under a structural pattern, acting as a wrapper to the existing class.

This pattern creates a decorator class that wraps the original class and provides additional functionality, keeping the class methods signature intact. In other words, The Decorator Pattern uses composition instead of inheritance to extend the functionality of an object at runtime.

When do we need the decorator design pattern?

  • When one has an object that requires functionality extension, for extending functionality, decorators are a versatile alternative to subclassing.
  • When one has to recursively rewrap or change the functionalities of an object according to the requirements, dynamically without affecting other objects of the class.

Elements of the decorator pattern

A typical diagram of a decorator pattern looks like this:

A typical diagram of a decorator pattern

Following are the participants of the Decorator Design pattern:

  • Component – this is the wrapper that can have additional responsibilities associated with it at runtime.
  • Concrete component– is the original object to which the additional responsibilities are added in the program.
  • Decorator-this is an abstract class that contains a reference to the component object and also implements the component interface.
  • Concrete decorator-they extend the decorator and builds additional functionality on top of the Component class.

Problem

Suppose we want to implement different kinds of cars – we can create an interface Car to define the assembling method, and then we can have a Basic car, furthermore, we can extend it to a Water car and Flying Car. But if we want to get a car at runtime that has both the features of a water car and a flying car, then the implementation gets complex, and if, furthermore, we want to specify which features should be added first, it gets even more complex. Now imagine if we have ten different kinds of cars (Sports, Luxury, Economy cars…), the implementation logic using inheritance and composition will be impossible to manage.

Main and sub classes
Solution

To solve this kind of programming situation, we apply the decorator pattern in java:

1. Component Interface – The interface or abstract class defining the methods that will be implemented. In our case, the Car will be the component interface.

package com.atlantbh.design.decorator;

public interface Car {

    public void assemble();
}

2. Component Implementation – The basic implementation of the component interface. We can have BasicCar class as our component implementation.

package com.atlantbh.design.decorator;

public class BasicCar implements Car {

    @Override
    public void assemble() {
        System.out.print("Basic Car.");
    }

}

3. Decorator – The decorator class implements the component interface and has a relationship with the component interface. The component variable should be accessible to the child decorator classes, so we will protect this variable.

package com.atlantbh.design.decorator;

public class CarDecorator implements Car {

    protected Car car;
    
    public CarDecorator(Car c){
        this.car=c;
    }
    
    @Override
    public void assemble() {
        this.car.assemble();
    }

}

4. Concrete Decorators – Extending the base decorator functionality and modifying the component behavior accordingly. We can have concrete decorator classes such as FlyingCar and WaterCar.

package com.atlantbh.design.decorator;

public class FlyingCar extends CarDecorator {

    public FlyingCar(Car c) {
        super(c);
    }

    @Override
    public void assemble(){
        super.assemble();
        System.out.print(" Adding features of Flying Car.");
    }
}
package com.atlantbh.design.decorator;

public class WaterCar extends CarDecorator {

    public WaterCar(Car c) {
        super(c);
    }
    
    @Override
    public void assemble(){
        super.assemble();
        System.out.print(" Adding features of Water Car.");
    }
}


Test program

package com.atlantbh.design.test;

import com.atlantbh.design.decorator.BasicCar;
import com.atlantbh.design.decorator.Car;
import com.atlantbh.design.decorator.FlyingCar;
import com.atlantbh.design.decorator.WaterCar;

public class DecoratorPatternTest {

    public static void main(String[] args) {
        Car waterCar = new WaterCar(new BasicCar());
        waterCar.assemble();
        System.out.println("\n*****");
        
        Car hybridCar = new WaterCar(new FlyingCar(new BasicCar()));
        hybridCar.assemble();
    }

}

You can notice that our program can create different kinds of objects at runtime, and they can specify the order of execution.

The output of the above program is:

Basic Car. Adding features of Water Car.
*****
Basic Car. Adding features of Flying Car. Adding features of Water Car.

The Decorator lets you structure your business logic into layers, create a decorator for each layer, and compose objects with various combinations of this logic at runtime. The client code can treat all these objects similarly since they all follow a common interface.

Many programming languages have the final keyword that can be used to prevent further extension of a class. For a final class, the only way to reuse the existing behavior would be to wrap the class with your own wrapper, using the Decorator pattern.


“Decorator pattern” Tech Bite was brought to you by Safet Pojskić, Junior Software Engineer at Atlantbh.

Tech Bites are tips, tricks, snippets or explanations about various programming technologies and paradigms, which can help engineers with their everyday job.

Decorator pattern
Software DevelopmentTech Bites
January 23, 2023

Decorator pattern

Design patterns are typical solutions to common problems in software design. Each pattern is like a blueprint that you can customize to solve a particular design problem in your code. A decorator pattern allows users to add new functionality to an existing object without altering its structure. This design pattern comes under…

Leave a Reply