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.
Read Next
- For composition-first design, continue with the Composition vs Inheritance Guide.
- For behavior substitution, compare it with the Strategy Pattern Guide.
While AdSense review is pending, related guides are shown instead of ads.
Start Here
Continue with the core guides that pull steady search traffic.
- Middleware Troubleshooting Guide: Redis vs RabbitMQ vs Kafka A practical middleware troubleshooting guide for developers covering when to reach for Redis, RabbitMQ, or Kafka symptoms first, and which problem patterns usually belong to each tool.
- Kubernetes CrashLoopBackOff: What to Check First A practical Kubernetes CrashLoopBackOff troubleshooting guide covering startup failures, probe issues, config mistakes, and what to inspect first.
- Kafka Consumer Lag Increasing: Troubleshooting Guide A practical Kafka consumer lag troubleshooting guide covering what lag usually means, which consumer metrics to check first, and how poll timing, processing speed, and fetch patterns affect lag.
- Kafka Rebalancing Too Often: Common Causes and Fixes A practical Kafka troubleshooting guide covering why consumer groups rebalance too often, what poll timing and group protocol settings matter, and how to stop rebalances from interrupting useful work.
- Docker Container Keeps Restarting: What to Check First A practical Docker restart-loop troubleshooting guide covering exit codes, command failures, environment mistakes, health checks, and what to inspect first.
While AdSense review is pending, related guides are shown instead of ads.