Software Design Pattern: Decorator Pattern

Software Design Pattern: Decorator Pattern

The decorator pattern is a structural pattern that provides a wrapper to the existing class. It allows us to dynamically add functionality and behavior to an object without affecting the behavior of other existing objects within the same class. We use inheritance to extend the behavior of the class. This takes place at compile-time, and all the instances of that class get the extended behavior.

Example:

A pizza is an actual illustration of a decorator design motif. Here, the basis of the pizza would be the original class, and the assortment of toppings would serve as the additional functionality. The pizza foundation (original class) will not be altered by the addition of any toppings (functionalities) by the consumer.

Another illustration would be to dress a human body. The functions of clothing are additions to the original class of the human body. Although you can choose to wear various types of clothing, the human body (original class) will not change.

Code:

ChristmasTree.java

public interface ChristmasTree {
    String decorate();
}

ChristmasTreeImpl.java

public class ChristmasTreeImpl implements ChristmasTree {
    @Override
    public String decorate() {
        return "Christmas tree";
    }
}

TreeDecorator.java

public abstract class TreeDecorator implements ChristmasTree {
    private final ChristmasTree tree;

    public TreeDecorator(ChristmasTree tree) {
        this.tree = tree;
    }

    @Override
    public String decorate() {
        return tree.decorate();
    }
}

BubbleLights.java

public class BubbleLights extends TreeDecorator {
    public BubbleLights(ChristmasTree tree) {
        super(tree);
    }

    @Override
    public String decorate() {
        return super.decorate() + decorateWithBubbleLights();
    }

    private String decorateWithBubbleLights() {
        return " with Bubble Lights";
    }
}

Tinsel.java

public class Tinsel extends TreeDecorator {
    public Tinsel(ChristmasTree tree) {
        super(tree);
    }

    @Override
    public String decorate() {
        return super.decorate() + decorateWithTinsel();
    }

    private String decorateWithTinsel() {
        return " with Tinsel";
    }
}

main.java

public static void main(String[] args) {
  ChristmasTree tree1 = new BubbleLights(new Tinsel(new ChristmasTreeImpl()));
  System.out.println(tree1.decorate());
}

Output:

Christmas tree with Tinsel with Bubble Lights

Explain:

  • The ChristmasTree interface defines the method decorate(), which returns a string. The ChristmasTreeImpl class implements this interface and returns the string “Christmas tree”.

  • The TreeDecorator abstract class implements the ChristmasTree interface and has a constructor that takes a ChristmasTree object. It also has an implementation of the decorate() method that simply calls the same method on the wrapped object.

  • The BubbleLights and Tinsel classes both extend the TreeDecorator class and add their own decorations to the tree. They both have constructors that take a ChristmasTree object and call the constructor of their superclass with that object. They also override the decorate() method to add their own decorations to the string returned by the wrapped object.

  • Finally, in the main method, we create a new ChristmasTreeImpl object and wrap it in a Tinsel object and then a BubbleLights object. When we call the decorate() method on this object, it will return “Christmas tree with Tinsel with Bubble Lights”.

Follow me on : Github Linkedin