Decorator Pattern

Add behavior to one object by wrapping it in another object with the same interface, so features stack at run time instead of multiplying subclasses.

Overview

The problem it solves A class needs optional extras: compression, encryption, logging, a cache. With subclasses, every combination is a new class, and the combinations grow fast. A decorator is an object that implements the same interface as the thing it wraps, adds one behavior, and passes everything else through. You pick the extras when you build the object, in any order, and the caller cannot tell a wrapped object from a plain one.

The pattern is a structural pattern from the Gang of Four book (Gamma, Helm, Johnson and Vlissides, *Design Patterns*, 1994). The book's roles are Component (the shared interface), ConcreteComponent (the plain object), Decorator (a base that holds a component and forwards to it) and ConcreteDecorator (one extra behavior).

  • 3 — 8 — 3
  • 4 — 16 — 4
  • 10 — 1,024 — 10

The subclass column is 2 to the power of the number of features, including the plain object. The arithmetic is why the pattern exists.

How a call travels through the stack Each decorator does its part and hands the call to the object inside it. On the way back out, each layer sees the inner result and may change it. The order of the layers is the order of the work.

A runnable example The store saves a string and loads it back. Two decorators add compression and a toy cipher. The caller builds the stack once and then uses it as a plain Store.

Nothing in MemoryStore changed when the two features were added, and no CompressedShiftedMemoryStore class exists.

Decorators you already use | Library | Component | Decorators | |---|---|---| | Java I/O | InputStream | FilterInputStream wraps another input stream and forwards to it; its subclasses BufferedInputStream, DataInputStream and PushbackInputStream each add one capability | | Web frameworks | A request handler | Middleware layers for logging, authentication and rate limits, each wrapping the next handler | | Python functions | A function | @wrapper syntax applies a function that returns a new function |

Python's @ syntax is the same idea applied to functions instead of objects. The docs define a decorator as a function returning another function and call the syntax sugar for f = wrapper(f). The wrapper takes the original's place while adding behavior, which is the decorator shape. functools.wraps copies the original's name, doc and other metadata onto the wrapper and stores the original in wrapped, so tools that inspect the function still see the real one.

Decorator next to its look-alikes | Pattern | Same interface as the wrapped object? | Who creates the wrapped object | Intent | |---|---|---|---| | Decorator | Yes | The caller, which then stacks as many decorators as it needs | Add behavior | | Proxy | Yes | Often the proxy itself (virtual, remote), one layer | Control access | | Adapter | No, it changes the interface | The caller, or whoever owns the adaptee | Make an existing class fit | | Strategy | Not a wrapper; a part held inside the object | The object's owner picks the strategy | Swap an algorithm |

What you will be able to do - Explain the subclass explosion with numbers and show how decorators avoid it. - Write a base decorator and two concrete decorators that stack in either order. - Trace a call through a stack and say why layer order matters. - Connect the pattern to Java's stream classes and Python's function decorators. - Tell Decorator from Proxy, Adapter and Strategy in one sentence each.

Interview questions

  • What are the parts of the Decorator pattern, and why does the decorator base class exist?
  • Show the smell that Decorator removes, and the fix.
  • How does Decorator differ from inheritance, and when is inheritance still the better choice?
  • What goes wrong with deep decorator stacks?
  • When would you choose Decorator over Strategy?

Sign up free to read the full topic

More Low-Level Design topics

Start free on Cariara · Coding problems · System design studio