Decorator Design Pattern in Java

This article explains Decorator design pattern in java with UML class diagram. It then takes an example scenario in java and explains it with class diagram and code.
Introduction Decorator Design Pattern is a structural design pattern among the Gang Of Four(GOF) Design PatternsClick to read: Gang Of Four(GOF) Design Patterns. Being a structural design pattern, the Decorator pattern deals with how objects are composed together - here, by wrapping one object in another which adds behaviour to it. What is Decorator Design Pattern The Decorator design pattern attaches additional responsibilities to an object dynamically, at runtime, while keeping the object's interface the same. A decorator implements the same interface as the object it wraps, forwards calls to it, and adds its own behaviour before or after forwarding. Because a decorator looks exactly like the object it wraps, decorators can be stacked - a decorator can wrap another decorator - and the client cannot tell the difference. This gives a flexible alternative to subclassing: instead of a subclass for every combination of features, each feature is one small decorator class, and combinations are assembled at runtime. Scenarios in which Decorator Design Pattern can be used
  1. Responsibilities added to individual objects: When behaviour should be added to some objects of a class, not all of them, and possibly at runtime - for example, buffering or compression for one particular stream.
  2. Too many combinations for subclassing: When features can be combined freely, so that subclassing would need a class for every combination - a "class explosion".
  3. Responsibilities which can be withdrawn: When features should be easy to add and remove without changing the class being decorated, which may not even be open to modification.
Class Diagram for Decorator Design Pattern
Decorator Design Pattern Class Diagram
Explanation of Decorator Design Pattern's Class Diagram
  • Component is the interface shared by the objects being decorated and the decorators themselves.
  • ConcreteComponent is the original object to which responsibilities are added.
  • Decorator is an abstract class which implements Component and holds a reference to a Component - the object it wraps. Its operation() forwards to the wrapped component.
  • ConcreteDecoratorA and ConcreteDecoratorB extend Decorator, adding state or behaviour before or after forwarding the call.
  • Client works only with the Component interface, so it can use a plain component or one wrapped in any number of decorators in exactly the same way.
Decorator Design Pattern in Java - Class Diagram
Decorator Design Pattern in Java Class Diagram
The example is a coffee shop. A beverage can have any combination of add-ons - milk, caramel, or a double shot of either - and each add-on changes both the description and the price. With subclasses, every combination would need its own class; with decorators, each add-on is one class.
Code for the classes shown in Java Example's Class Diagram
Beverage.java - the component interface
//Beverage.java
public interface Beverage {
 String description();
 double cost();
}
Espresso.java - the concrete component
//Espresso.java
public class Espresso implements Beverage {
 public String description() { return "Espresso"; }
 public double cost() { return 120.00; }
}
BeverageDecorator.java - the abstract decorator
//BeverageDecorator.java
public abstract class BeverageDecorator implements Beverage {
 protected final Beverage beverage;            // the wrapped beverage

 protected BeverageDecorator(Beverage beverage) {
  this.beverage = beverage;
 }
 public String description() { return beverage.description(); }
 public double cost() { return beverage.cost(); }
}
Milk.java and Caramel.java - the concrete decorators
//Milk.java
public class Milk extends BeverageDecorator {
 public Milk(Beverage beverage) { super(beverage); }
 public String description() { return beverage.description() + ", milk"; }
 public double cost() { return beverage.cost() + 30.00; }
}
//Caramel.java
class Caramel extends BeverageDecorator {
 public Caramel(Beverage beverage) { super(beverage); }
 public String description() { return beverage.description() + ", caramel"; }
 public double cost() { return beverage.cost() + 45.50; }
}
CoffeeShop.java (with the main() method)
//CoffeeShop.java
public class CoffeeShop {
 static void serve(Beverage b) {
  System.out.printf("%-40s Rs. %.2f%n", b.description(), b.cost());
 }

 public static void main(String[] args) {
  serve(new Espresso());                                  // plain
  serve(new Milk(new Espresso()));                        // one decorator
  serve(new Caramel(new Milk(new Espresso())));           // decorators stacked
  serve(new Caramel(new Caramel(new Milk(new Espresso()))));   // the same decorator twice
 }
}
 OUTPUT on running CoffeeShop.java
Espresso                                 Rs. 120.00
Espresso, milk                           Rs. 150.00
Espresso, milk, caramel                  Rs. 195.50
Espresso, milk, caramel, caramel         Rs. 241.00
Explanation of Java Example's Class Diagram & Code
  • Beverage is the component interface, and Espresso the concrete component.
  • BeverageDecorator is the decorator: it implements Beverage and wraps another Beverage, forwarding to it by default.
  • Milk and Caramel are concrete decorators. Each calls the wrapped beverage and adds its own part - ", milk" to the description, Rs. 30 to the cost.
  • In new Caramel(new Milk(new Espresso())), a call to cost() goes to Caramel, which calls Milk, which calls Espresso; the results are added on the way back. serve() cannot tell a decorated beverage from a plain one.
  • Two classes of add-ons produce any combination, including the same add-on twice - something subclassing could not express without a class for every case.
Decorator in the JDK: java.io streams The best-known use of the Decorator pattern in Java is the java.io package. InputStream is the component; FileInputStream and ByteArrayInputStream are concrete components; and FilterInputStream is the decorator base class, extended by BufferedInputStream, DataInputStream, GZIPInputStream and others -
java.io streams decorating each other
//StreamDecorators.java
import java.io.*;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

public class StreamDecorators {
 public static void main(String[] args) throws IOException {
  ByteArrayOutputStream bytes = new ByteArrayOutputStream();
  // DataOutputStream decorates GZIPOutputStream, which decorates the byte array stream
  try (DataOutputStream out = new DataOutputStream(new GZIPOutputStream(bytes))) {
   out.writeUTF("Decorator pattern");
   out.writeInt(1994);
  }
  System.out.println("Compressed size: " + bytes.size() + " bytes");

  // Reading back with the same decorators, in the same order
  try (DataInputStream in = new DataInputStream(new BufferedInputStream(
      new GZIPInputStream(new ByteArrayInputStream(bytes.toByteArray()))))) {
   System.out.println("Read back: " + in.readUTF() + ", " + in.readInt());
  }
 }
}
 OUTPUT on running StreamDecorators.java
Compressed size: 43 bytes
Read back: Decorator pattern, 1994
Each wrapper adds one responsibility - compression, buffering, reading primitive types - and they can be combined in any order the application needs. Collections.unmodifiableList() and Collections.synchronizedList() are decorators too: they wrap a list and change the behaviour of its methods.
Summary In the above tutorial we understood what is Decorator design pattern and the main scenarios in which it is applicable. We then looked at the UML class diagram for Decorator Design Pattern & its explanation, a Java use case implementing the Decorator pattern for a coffee shop with its class diagram, code and output, followed by an explanation of both the class diagram & code. Lastly, we saw how the java.io streams are built on the Decorator pattern. This concludes the tutorial on Decorator design pattern.

Decorator Design Pattern in Java - JavaBrahman