For AI agents: the complete documentation index is available at https://nhanbe.id.vn/llms.txt, the full documentation bundle is available at https://nhanbe.id.vn/llms-full.txt, and this page is available as Markdown at https://nhanbe.id.vn/design-patterns/structural/decorator.md.

Attach additional responsibilities to an object dynamically by wrapping it. You need flexible, runtime composition of behavior. Inheritance would

Decorator

Overview

Attach additional responsibilities to an object dynamically by wrapping it.

When to use

  • You need flexible, runtime composition of behavior.
  • Inheritance would lead to too many subclasses.

Java example

interface Notifier {
    void send(String message);
}

class EmailNotifier implements Notifier {
    public void send(String message) { /* email */ }
}

class SmsDecorator implements Notifier {
    private final Notifier wrapped;
    SmsDecorator(Notifier wrapped) { this.wrapped = wrapped; }
    public void send(String message) {
        wrapped.send(message);
        /* sms */
    }
}

TypeScript example

interface Notifier {
  send(message: string): void;
}

class EmailNotifier implements Notifier {
  send(message: string): void {}
}

class SmsDecorator implements Notifier {
  constructor(private wrapped: Notifier) {}
  send(message: string): void {
    this.wrapped.send(message);
  }
}

Pros and cons

Pros:

  • Behavior can be added without subclassing.
  • Multiple decorators can be combined.

Cons:

  • Many small objects can be hard to debug.
  • Order of decorators matters.

Common pitfalls

  • Hiding side effects in deep decorator stacks.
  • Mutating shared state across decorators.