Decorator Pattern

What is the Decorator pattern?

Decorator means: wrap an object to add behavior at runtime, instead of baking that behavior into a subclass.

Every decorator implements the same interface as the thing it wraps, holds a reference to it, and adds a little extra work before or after forwarding the call. Because a decorator implements the same interface it wraps, decorators stack: you can wrap a decorator in another decorator, and the caller never needs to know how many layers are underneath.

To build one you usually need:

  1. A shared interface (the thing being decorated).
  2. A base decorator that holds the wrapped object and forwards calls to it.
  3. Concrete decorators that override a method to add their own bit of behavior.

The problem it solves

Say drinks can optionally get milk, sugar, or whipped cream, in any combination.

Solving this with subclassing needs a class for every combination: MilkCoffee, SugarCoffee, MilkSugarCoffee, and so on. Three optional toppings already produce eight classes, and each new topping doubles that count again. Decorator solves the same problem with composition instead of a subclass per combination.


How it works

Start with a plain implementation and an abstract decorator that forwards every call by default. Each concrete decorator overrides the methods to add its own contribution.

Each call walks down through the stack: SugarDecorator adds its bit to whatever MilkDecorator returns, which adds its bit to the base coffee. No single class knows the full combination — each one only knows its own small addition, plus how to ask the next layer for the rest.

This is the same shape behind Java’s BufferedInputStream and DataInputStream wrapping a FileInputStream: each layer adds one capability on top of the one before it.


Worked example: decorating an HTTP request

Add authentication and logging to an outgoing request without hardcoding either into the request class.

Neither decorator touched BasicHttpRequest. You can drop either one out of the stack, or add a retry or timing decorator later, without editing anything that already exists.


When it helps

Decorator earns its place when features are genuinely optional and combinable — a coffee with any subset of toppings, or a request that can optionally be authenticated, logged, or retried in any mix.

It also helps when you cannot or should not modify the original class because it is shared, generated, or owned by someone else.


When it hurts

If a feature is not really optional — every caller needs it, always — just put it in the base class.

Decorator assumes each wrapper’s behavior is independent of the others. If your “features” actually depend on each other or conflict in certain orders, plain conditional logic is often more honest than a decorator chain. Long decorator stacks are also genuinely hard to debug — keep them short.


Decorator vs. interceptor

“Interceptor” describes something similar — code that runs before or after an operation for logging, auth, or timing. The difference is ownership.

Decorator is code you compose yourself around an interface you own. Interceptor usually names a role inside a framework’s pipeline — a servlet filter, a Spring HandlerInterceptor — where the framework decides the order and dispatches through it. If you own the composition, call it a decorator. If you are plugging into a framework’s pipeline, “interceptor” or “middleware” is the expected term.


Practical tips

  • Keep each decorator responsible for exactly one thing.
  • Every decorator must implement the same interface as the thing it wraps.
  • Never let a decorator mutate the state of the object it wraps — wrap and forward only.
  • Avoid decorating for the sake of it: a small dedicated class beats a two-layer decorator stack for one fixed combination.

Check your understanding

What is Decorator, in one sentence?
A pattern that adds behavior to an object at runtime by wrapping it in one or more objects that share its interface.

Why not just use subclassing?
Subclassing needs a new class for every combination of optional features. Decorator combines features by stacking wrappers instead.

Can decorators be stacked in any order?
Usually yes, as long as the behaviors are independent. If they depend on each other, order starts to matter.

What is the biggest risk with Decorator?
Stacking too many layers makes the code hard to trace, and decorators that secretly depend on each other’s order undermine the point of composable behavior.


What to learn next

Next: Facade Pattern — give callers one simple door into a subsystem made of many objects.