BackendBench
Coffee

Unravel › Java concepts › Design patterns and architecture

Java language

Decorator and wrapper classes

A decorator wraps an object of the same interface and adds behaviour before or after delegating: new BufferedReader(new FileReader(f)) adds buffering to a plain reader.

Learn these first

At a glance

Mental model

Gift wrapping: the present is still the same present inside, but each layer adds something (paper, ribbon, card), and layers can be added or left out in any combination.

What happens, in order
  1. 1The wrapper implements the same interface as the thing it wraps, holds a reference to it, forwards each call, and adds its own work: logging, caching, timing, retry, access checks
  2. 2Because the type stays the same, callers cannot tell the difference, and you can combine decorators: new Logged(new Cached(new RealService()))
  3. 3The Java I/O library is built from decorators (BufferedInputStream, GZIPInputStream, DataInputStream)Collections.unmodifiableList and Collections.synchronizedList wrap a list the same way.
  4. 4Spring proxies for @Transactional and @Cacheable are decorators created at run time
Remember
Watch out
Forgetting to delegate one of the methodsWrapping in the wrong orderClosing only the outer wrapper when the inner one needs it too (usually the outer closes the inner)
Example (highlighted lines explain it)
public class Decorators {  interface Service { String find(String id); }  static class Real implements Service { public String find(String id) { return "item-" + id; } }  static class Logged implements Service {    private final Service inner;    Logged(Service inner) { this.inner = inner; }    public String find(String id) {      System.out.println("find " + id);      return inner.find(id);    }  }  static class UpperDecorator extends java.io.FilterInputStream {    UpperDecorator(java.io.InputStream in) { super(in); }  }}
Read the full explanation (about 1 min)

A decorator wraps an object of the same interface and adds behaviour before or after delegating: new BufferedReader(new FileReader(f)) adds buffering to a plain reader. Wrappers can be stacked.

A mental model

Gift wrapping: the present is still the same present inside, but each layer adds something (paper, ribbon, card), and layers can be added or left out in any combination.

How it works

The wrapper implements the same interface as the thing it wraps, holds a reference to it, forwards each call, and adds its own work: logging, caching, timing, retry, access checks.

Because the type stays the same, callers cannot tell the difference, and you can combine decorators: new Logged(new Cached(new RealService())).

The Java I/O library is built from decorators (BufferedInputStream, GZIPInputStream, DataInputStream). Collections.unmodifiableList and Collections.synchronizedList wrap a list the same way.

Spring proxies for @Transactional and @Cacheable are decorators created at run time.

Going deeper

When the interface is large, writing a decorator is tedious: extend a forwarding base class (ForwardingList in Guava, FilterInputStream in the JDK) and override only the methods you change.

The order of decorators matters: retry outside a timeout behaves differently from a timeout outside a retry.

Compare with inheritance: a decorator can be applied at run time to any implementation, while a subclass fixes the combination at compile time.

Under the hood

It is just object composition and delegation. A call to the outer decorator runs its code and then calls the inner object; depth of the chain equals the number of extra method calls.

Common mistakes

  • Forgetting to delegate one of the methods.
  • Wrapping in the wrong order.
  • Closing only the outer wrapper when the inner one needs it too (usually the outer closes the inner).

How Unravel recognises it

When you paste code, Unravel looks for these signs of decorator and wrapper classes. Spotting them yourself is a good habit when you read unfamiliar code:

Part of a learning path

Related concepts

Frequently asked questions

What is Decorator and wrapper classes?

A decorator wraps an object of the same interface and adds behaviour before or after delegating: new BufferedReader(new FileReader(f)) adds buffering to a plain reader. Wrappers can be stacked.

What are the most common mistakes with Decorator and wrapper classes?

Forgetting to delegate one of the methods. Wrapping in the wrong order. Closing only the outer wrapper when the inner one needs it too (usually the outer closes the inner).

How can I see Decorator and wrapper classes in real code?

Paste your own Java into Unravel and it will point out where decorator and wrapper classes appears, explain how it works at the detail level you choose, and draw the flow of the code around it.