Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.
The Decorator pattern is a structural design pattern that lets you wrap an object inside another object to add new behaviour to it — without modifying the original object's class and without creating a subclass for every combination of features.
Each decorator wraps the component, adds its own behaviour before or after delegating work to the wrapped object. You can stack multiple decorators on top of each other, creating a pipeline of responsibilities that are applied in order.
Think of it like wearing clothes: you are the base object. You put on a jacket (one decorator), then a scarf (another decorator), then gloves (yet another). Each layer adds new capability, and you can mix and match freely without creating a new "JacketScarfGlovePerson" class.
Key Idea: Extend an object's behaviour at runtime by wrapping it, rather than using inheritance. Each wrapper adds one focused responsibility.
The common interface for both the real object and all its decorators. This is what makes them interchangeable — decorators implement the same interface as the object they wrap, so the client can't tell the difference.
The real, base object that does the core work. Decorators wrap this. It has no knowledge of being decorated — it simply fulfils the component interface.
An abstract class that implements the component interface and holds a reference to a wrapped component object. It delegates all operations to the wrapped object — concrete decorators override only what they need to change.
Adds specific extra behaviour before or after calling the wrapped object's method. Multiple concrete decorators can be stacked on the same component in any order.
Suppose you have a Coffee class and you want to support add-ons: Milk, Sugar, and Whip. Using inheritance, you'd need a separate subclass for every combination:
With just 3 add-ons you need 7 subclasses. Add a 4th add-on → 15 classes. This grows as 2ⁿ. Completely unmanageable.
// Problem: Subclass explosion using inheritance
class Coffee { ... }
class CoffeeWithMilk extends Coffee { ... }
class CoffeeWithSugar extends Coffee { ... }
class CoffeeWithMilkAndSugar extends Coffee { ... }
class CoffeeWithMilkAndWhip extends Coffee { ... }
// ... 2^n subclasses for n add-ons — impossible to maintain!// Solution: Compose behaviours by wrapping at runtime Coffee coffee = new SimpleCoffee(); coffee = new MilkDecorator(coffee); // add milk coffee = new SugarDecorator(coffee); // add sugar coffee = new WhipDecorator(coffee); // add whip // Any combination, zero new classes needed!
Both the real object and all decorators implement the same interface, making them interchangeable to any client code.
This class holds a reference to a wrapped component and delegates all method calls to it — a forwarding proxy by default.
Each concrete decorator overrides only the method it needs, adding behaviour before or after calling super.method().
Wrap the base object with as many decorators as needed, in any order. Each call travels through the decorator chain automatically.
Call Chain (innermost to outermost)
Each decorator adds its behaviour, then passes the call inward. The result travels back outward.
Both add behaviour to a class — but they do it in fundamentally different ways:
| Aspect | Decorator | Inheritance |
|---|---|---|
| When applied | Runtime — wrap any time | Compile time — fixed at build |
| Combinations | Any order, unlimited stacking | Requires new subclass per combo |
| Class count | 1 class per behaviour | Grows as 2ⁿ combinations |
| Original class | Untouched, unmodified | May need modification |
| Principle | Composition over inheritance | IS-A relationship |
Rule of thumb: If you find yourself creating subclasses just to add optional features or combinations of behaviour, the Decorator pattern is almost always the better solution.
// Decorator Pattern — Coffee Order System
// Component interface
interface Coffee {
String getDescription();
double getCost();
}
// Concrete Component
class SimpleCoffee implements Coffee {
@Override
public String getDescription() {
return "Simple Coffee";
}
@Override
public double getCost() {
return 50.0;
}
}
// Base Decorator — wraps a Coffee object
abstract class CoffeeDecorator implements Coffee {
protected Coffee decoratedCoffee;
public CoffeeDecorator(Coffee coffee) {
this.decoratedCoffee = coffee;
}
@Override
public String getDescription() {
return decoratedCoffee.getDescription();
}
@Override
public double getCost() {
return decoratedCoffee.getCost();
}
}
// Concrete Decorators
class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
return decoratedCoffee.getDescription() + ", Milk";
}
@Override
public double getCost() {
return decoratedCoffee.getCost() + 15.0;
}
}
class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
return decoratedCoffee.getDescription() + ", Sugar";
}
@Override
public double getCost() {
return decoratedCoffee.getCost() + 5.0;
}
}
class WhipDecorator extends CoffeeDecorator {
public WhipDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
return decoratedCoffee.getDescription() + ", Whip Cream";
}
@Override
public double getCost() {
return decoratedCoffee.getCost() + 20.0;
}
}
// Usage — stack decorators at runtime
public class CoffeeShop {
public static void main(String[] args) {
Coffee coffee = new SimpleCoffee();
System.out.println(coffee.getDescription() + " = ₹" + coffee.getCost());
// Simple Coffee = ₹50.0
coffee = new MilkDecorator(coffee);
System.out.println(coffee.getDescription() + " = ₹" + coffee.getCost());
// Simple Coffee, Milk = ₹65.0
coffee = new SugarDecorator(coffee);
coffee = new WhipDecorator(coffee);
System.out.println(coffee.getDescription() + " = ₹" + coffee.getCost());
// Simple Coffee, Milk, Sugar, Whip Cream = ₹90.0
}
}When objects need optional add-ons that should be combinable in any order — like toppings on a pizza or filters on an image.
PizzaTopping, ImageFilter, TextFormatterWhen you want to add logging, caching, authentication, or retry logic around existing operations without cluttering business logic.
LoggingDecorator, CacheDecorator, AuthDecoratorWhen processing data through multiple transformation steps — compress, then encrypt, then encode — each step being a decorator.
BufferedReader, GZIPOutputStream, CipherStreamWhen building request/response pipelines where each middleware wraps the next — exactly how Express.js and Django middleware work.
Express middleware, Django middleware, KoaWhen a UI widget needs optional visual additions like scroll bars, borders, shadows, or tooltips without subclassing each combination.
ScrollableWidget, BorderedWidget, TooltipWrapperJava's entire java.io package is built on the Decorator pattern. new BufferedReader(new InputStreamReader(new FileInputStream("file.txt"))) — each layer wraps the previous one, adding buffering, character decoding, and file reading.
Python's built-in @decorator syntax is a direct language implementation of this pattern. @login_required, @cache_page, and @staticmethod in Django are all decorators wrapping functions with extra behaviour.
Express middleware like app.use(cors()), app.use(helmet()), app.use(morgan()) — each middleware wraps the request handler, adding CORS, security headers, and logging respectively.
HOCs like withAuth(Component), withRouter(Component), and connect(mapState)(Component) from Redux are classic Decorator implementations — they wrap a component and inject extra props or behaviour.
NestJS uses TypeScript decorators extensively — @Controller(), @Injectable(), @Get() — each one wraps the class or method to attach metadata and behaviour without touching the underlying logic.