The Decorator Pattern in Java

Introduction to the Decorator Pattern

Design pattern in a simple meaning, is a way to design reusable object-oriented code. We can think of ways to design our classes, interfaces, enums and their members so that the code is reusable but flexible.

There are 23 most important design patterns, pioneered by the book Design Patterns: Elements of Reusable Object-Oriented Software, written by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides or Gang of Four for short. We will cover these design pattern one by one.

The decorator pattern attaches additional functionality to an object dynamically, without changing the original class or subclassing the class. We know that we can use inheritance or composition to add more behavior to an object. But this is done before compile time, with the Decorator pattern we can do that at runtime.

Implementation of Decorator Pattern in Java

The Decorator pattern is rather complex in comparison to other patterns, so we should look into the example. Say we have a Ring class in Ring.java (in src folder of course):

public class Ring {
  private String name;
  private double cost;
  public Ring(String name, double cost){
    this.name = name;
    this.cost = cost;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public double getCost(){
    return cost;
  }
  public void setCost(double cost){
    this.cost = cost;
  }
}

A normal encapsulated class. In fact this should be an abstract class and then the concrete Ring class will extend this abstract class. But for simplicity, we will keep this as the base class for the Decorator pattern.

Now say that we want the Ring object to have new functionalities. The customer can choose the big size with fix cost 3 $, or choose to engrave names with fix cost 1 $. With each added property, the cost and the name of the Ring must be updated.

Of course we can easily do this with inheritance, say we create an EngravingRing class and set its name and its cost. But what happens if we have a lot of Jewelry classes and each has two options just like the Ring class. Should we create a lot of Engraving-Jewelry subclasses? Now, there is a better way with the Decorator pattern.

First we create an abstract class, the Decorator class in src folder:

public abstract class Decorator extends Ring{
  protected Ring ring;
  public Decorator(Ring ring) {
    super(ring.getName(), ring.getCost());
  }
}

What do we think of this class? Yep, it is basically a Ring class, but its constructor requires a Ring parameter. That’s the point of this pattern, we will discover this right now.

Now we create a BigDecorator that will add a big size option to the Ring object. This concrete class extend the Decorator:

public class BigDecorator extends Decorator{
  public BigDecorator(Ring ring) {
    super(ring);
    this.ring = ring;
    setCost(ring.getCost() + 3);
    setName(ring.getName() + " BIG SIZE");
  }
}

This class will receive a Ring object as the parameter, and returns a new Ring object with updated name and updated cost (add 3 $). Next we create the EngravingDecorator class:

public class EngravingDecorator extends Decorator{
  public EngravingDecorator(Ring ring) {
    super(ring);
    this.ring = ring;
    setCost(ring.getCost() + 1);
    setName(ring.getName() + " WITH ENGRAVING");
  }
}

This class will receive a Ring object as the parameter, and returns a new Ring object with updated name and updated cost (add 1 $). Now in Test.java:

public class Test {
  public static void main(String[] args){
    Ring ring = new Ring("BLUE MOON", 6);
    Ring ringBigSize = new BigDecorator(ring);
    Ring ringBigSizeEngraving =
        new EngravingDecorator(ringBigSize);
    //output the original ring
    System.out.println(ring.getName()
        + " - " + ring.getCost());
    //ring with big size
    System.out.println(ringBigSize.getName()
        + " - " + ringBigSize.getCost());
    //ring with big size and engraving names
    System.out.println(
        ringBigSizeEngraving.getName() +
        " - " + ringBigSizeEngraving.getCost());
  }
}

Run the code and we have the output:

BLUE MOON - 6.0
BLUE MOON BIG SIZE - 9.0
BLUE MOON BIG SIZE WITH ENGRAVING - 10.0

This is a very naive implementation of the Decorator pattern. But we can imagine that with the BigDecorator and EngravingDecorator classes, we can add functionalities to a lot of Ring-like classes. Yes, with just two classes we can do this, instead of using inheritance and then creating a lot of subclasses.

Leave a Reply