Decorator Pattern Guide: How Do You Add Behavior by Wrapping an Object?
Dev

Decorator Pattern Guide: How Do You Add Behavior by Wrapping an Object?


In real systems, you often want to keep an object’s core behavior but add something around it, like logging, permission checks, or caching. If you try to solve that with inheritance alone, the structure can become rigid. The decorator pattern is a classic alternative.

In this post, we will cover:

  • what the decorator pattern is
  • why it often comes up instead of inheritance
  • when it fits well
  • how it relates to composition

The key idea is that the decorator pattern extends behavior by wrapping an existing object, so you can add functionality without modifying the original object directly.

What is the decorator pattern?

The decorator pattern creates a wrapper object that exposes the same interface as the original object while adding behavior before or after the original action.

In simple terms:

  • keep the original object
  • wrap it
  • add behavior around it

Why is it often used instead of inheritance?

If you keep stacking behavior through inheritance, combinations can explode and the hierarchy becomes rigid.

For example:

  • with logging
  • with caching
  • with logging and caching

That kind of class growth quickly becomes hard to manage.

Decorators make those add-on behaviors easier to combine.

When does it fit well?

  • when the core behavior should stay the same
  • when add-on behavior should be optional
  • when multiple enhancements may be combined

So it fits very well in “base behavior plus optional layers” situations.

How does it relate to composition?

The decorator pattern is a classic composition-first design. Instead of deepening an inheritance hierarchy, it extends behavior by wrapping another object.

That is why it connects so naturally with the idea of preferring composition over inheritance.

Common misunderstandings

1. Decorator just means syntax decoration

The name can mislead people, but this is a structural design pattern.

2. Inheritance is always the more object-oriented way to add behavior

For changeable add-on behavior, decorators are often much more flexible.

3. Decorators are only useful in large frameworks

Logging, authorization, and caching are already everyday examples where the pattern can be useful.

Simple Example

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

class BasicNotifier implements Notifier {
  send(message: string): void {
    console.log(message);
  }
}

class LoggingNotifier implements Notifier {
  constructor(private wrapped: Notifier) {}

  send(message: string): void {
    console.log('log start');
    this.wrapped.send(message);
  }
}

class SmsNotifier implements Notifier {
  constructor(private wrapped: Notifier) {}

  send(message: string): void {
    this.wrapped.send(message);
    console.log(`Send SMS: ${message}`);
  }
}

const notifier = new SmsNotifier(new LoggingNotifier(new BasicNotifier()));
notifier.send('Order completed');

The base behavior stays inside BasicNotifier, while logging and SMS delivery are layered on by wrapping it.

FAQ

Q. Where should beginners look for decorator opportunities?

Start where the core logic stays the same but add-on behavior needs to wrap it.

Q. Is decorator the same as strategy?

No. Strategy focuses more on replacing behavior, while decorator focuses more on adding layers around it.

Q. Can too many decorators become confusing?

Yes. That is why the boundaries and combination rules should stay intentional.

Start Here

Continue with the core guides that pull steady search traffic.