Back to Design Patterns
Structural Pattern

Decorator Pattern

Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.

Intermediate10 min read

What is the Decorator Pattern?

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.

Core Participants

Component

Component Interface

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.

Concrete Component

Concrete Component

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.

Base Decorator

Base Decorator

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.

Concrete Decorator

Concrete Decorator

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.

!Problem It Solves

The Subclass Explosion Problem

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:

  • → CoffeeWithMilk
  • → CoffeeWithSugar
  • → CoffeeWithWhip
  • → CoffeeWithMilkAndSugar
  • → CoffeeWithMilkAndWhip
  • → CoffeeWithSugarAndWhip
  • → CoffeeWithMilkSugarAndWhip

With just 3 add-ons you need 7 subclasses. Add a 4th add-on → 15 classes. This grows as 2ⁿ. Completely unmanageable.

Example Without Decorator

// 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 With Decorator

// 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!

How It Works — Step by Step

1

Define a Common Interface

Both the real object and all decorators implement the same interface, making them interchangeable to any client code.

2

Create a Base Decorator

This class holds a reference to a wrapped component and delegates all method calls to it — a forwarding proxy by default.

3

Concrete Decorators Override

Each concrete decorator overrides only the method it needs, adding behaviour before or after calling super.method().

4

Stack at Runtime

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)

Client
Decorator C
Decorator B
Decorator A
Real Object

Each decorator adds its behaviour, then passes the call inward. The result travels back outward.

Decorator vs Inheritance

Both add behaviour to a class — but they do it in fundamentally different ways:

AspectDecoratorInheritance
When appliedRuntime — wrap any timeCompile time — fixed at build
CombinationsAny order, unlimited stackingRequires new subclass per combo
Class count1 class per behaviourGrows as 2ⁿ combinations
Original classUntouched, unmodifiedMay need modification
PrincipleComposition over inheritanceIS-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.

Implementation

// 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 to Use Decorator

Adding Optional Features

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, TextFormatter

Cross-Cutting Concerns

When you want to add logging, caching, authentication, or retry logic around existing operations without cluttering business logic.

LoggingDecorator, CacheDecorator, AuthDecorator

I/O Stream Processing

When processing data through multiple transformation steps — compress, then encrypt, then encode — each step being a decorator.

BufferedReader, GZIPOutputStream, CipherStream

Middleware Pipelines

When building request/response pipelines where each middleware wraps the next — exactly how Express.js and Django middleware work.

Express middleware, Django middleware, Koa

UI Component Enhancement

When a UI widget needs optional visual additions like scroll bars, borders, shadows, or tooltips without subclassing each combination.

ScrollableWidget, BorderedWidget, TooltipWrapper

Pros & Cons

Advantages

  • Open/Closed Principle — extend behaviour without modifying existing code
  • Single Responsibility — each decorator handles one concern
  • Add or remove behaviours at runtime
  • Combine multiple behaviours by stacking decorators
  • Avoids feature-bloated parent classes

Disadvantages

  • Hard to remove a specific decorator from the middle of a stack
  • Order of decoration matters — wrong order = wrong behaviour
  • Can be difficult to debug deep decorator chains
  • Many small decorator classes can clutter the codebase
  • Initial setup is more complex than simple inheritance

Real-World Examples

Java I/O Streams

Java'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 @ Decorator Syntax

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.js Middleware (Node.js)

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.

React Higher-Order Components (HOC)

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.

TypeScript / NestJS Decorators

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.

What's Next?

Now that you understand Decorator, explore related Structural patterns: