← Back to Home
Decorator Pattern
Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.
What is it?
The Decorator pattern lets you wrap objects with new behavior at runtime without modifying the original class.
Example
interface Coffee { cost: () => number; description: () => string; } class BasicCoffee implements Coffee { cost() { return 5; } description() { return 'Basic Coffee'; } } class MilkDecorator implements Coffee { constructor(private coffee: Coffee) {} cost() { return this.coffee.cost() + 2; } description() { return this.coffee.description() + ', Milk'; } } class SugarDecorator implements Coffee { constructor(private coffee: Coffee) {} cost() { return this.coffee.cost() + 1; } description() { return this.coffee.description() + ', Sugar'; } } const coffee = new SugarDecorator(new MilkDecorator(new BasicCoffee())); console.log(coffee.description()); // "Basic Coffee, Milk, Sugar" console.log(coffee.cost()); // 8
Common Uses
- Adding features to components dynamically
- UI components (e.g., wrappers around buttons, inputs)
- Middleware chains (e.g., Express, Redux)
When to Use
- You want to add responsibilities to objects without subclassing
- Need to mix and match behaviors at runtime
Caution
- Too many small decorator classes can complicate maintenance
- Harder to trace functionality than with inheritance