What Problem It Solves
You need to add responsibilities to an object dynamically without modifying the original class. Subclassing every combination is explosive (BaseLogger + TimestampLogger + JSONLogger + EncryptionLogger = 2^N classes). Decorator wraps the original and adds behavior transparently.
Decorator vs Inheritance
| Aspect | Inheritance | Decorator |
|---|---|---|
| Number of classes | 2^N combinators (explosive) | N decorators + 1 component |
| Flexibility | Static (compile-time) | Dynamic (runtime-composable) |
| Stacking | Can't stack multiple features | Stack any combination |
| Transparency | New subclass per variant | Same interface, transparent wrapping |
Where Decorator Appears in LLD Problems
- Logger (#15): Add timestamp, log level, JSON format, encryption โ stack decorators
- Notification (#18): Add retry, batching, throttling, encryption as decorators around base channel
- Middleware (many): HTTP middleware wrapping request handler (auth โ logging โ rate limit โ handler)
- Pizza/Coffee customization (classic LLD example, not in problem list but commonly asked): size + toppings as decorators
Implementation
// โโโ EXAMPLE 1 โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// WHAT WE ARE IMPLEMENTING:
// A database stream writer wrapping files with Compression and Encryption
// layers at runtime.
//
// WHERE THE DECORATOR FITS IN:
// DataStream is the Component. FileDataStream is the Concrete Component.
// DataStreamDecorator is the base Decorator class wrapped by
// EncryptionDecorator.
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// --- Component ---
interface Logger {
void log(String message);
}
// --- Concrete component ---
class SimpleLogger implements Logger {
public void log(String message) {
System.out.println(message);
}
}
// --- Base decorator ---
abstract class LoggerDecorator implements Logger {
protected final Logger wrapped;
public LoggerDecorator(Logger wrapped) { this.wrapped = wrapped; }
public void log(String message) {
wrapped.log(message); // Default: delegate
}
}
// --- Concrete decorators ---
class TimestampDecorator extends LoggerDecorator {
public TimestampDecorator(Logger wrapped) { super(wrapped); }
public void log(String message) {
super.log("[2025-01-15 10:30:00] " + message);
}
}
class JsonDecorator extends LoggerDecorator {
public JsonDecorator(Logger wrapped) { super(wrapped); }
public void log(String message) {
super.log("{"message": "" + message + "", "level": "INFO"}");
}
}
class EncryptionDecorator extends LoggerDecorator {
public EncryptionDecorator(Logger wrapped) { super(wrapped); }
public void log(String message) {
String encrypted = "encrypted(" + message + ")";
super.log(encrypted);
}
}
public class Main {
public static void main(String[] args) {
// Compose decorators at runtime
Logger logger = new TimestampDecorator(
new JsonDecorator(
new EncryptionDecorator(
new SimpleLogger())));
logger.log("User login successful");
// Output: [2025-01-15 10:30:00] {"message": "encrypted(User login successful)", "level": "INFO"}
}
}Review
Help Us Improve
How helpful was this walkthrough?
Click a star to rate. We actively use this feedback to refine and update our system design content.
Placeholder
Discussion
Share your thoughts, ask questions, or help others.
Loading comments...