Decorator Pattern
Introduction
The Decorator pattern is a structural design pattern that allows behavior to be added to individual objects, either statically or dynamically, without affecting the behavior of other objects from the same class.
Purpose
- To add responsibilities to objects dynamically.
- To provide a flexible alternative to subclassing for extending functionality.
Use Cases
- When you need to add functionalities to specific objects dynamically and transparently, without affecting other objects.
- When extending functionality by subclassing is impractical or results in an excessive number of subclasses.
Advantages/Disadvantages
Advantages:
- More flexibility than static inheritance.
- Avoids feature-laden classes high up in the hierarchy.
- Easy to add and remove responsibilities.
Disadvantages:
- Can lead to a large number of small classes that can become overwhelming.
- Decorators and their enclosed components are not identical to the client.
Implementation in JavaScript
	class Component {
	  operation() {}
	}
	
	class ConcreteComponent extends Component {
	  operation() {
	    return 'ConcreteComponent';
	  }
	}
	
	class Decorator extends Component {
	  constructor(component) {
	    super();
	    this.component = component;
	  }
	
	  operation() {
	    return `Decorator(${this.component.operation()})`;
	  }
	}
	
	// Usage
	const simple = new ConcreteComponent();
	const decorated = new Decorator(simple);
	
	console.log(simple.operation()); // "ConcreteComponent"
	console.log(decorated.operation()); // "Decorator(ConcreteComponent)"Practical Example
Suppose we’re developing a logging system in a backend application where we want to extend the behavior of a logger dynamically to add timestamps, levels, or other metadata to the log messages.
	class Logger {
	  log(message) {
	    console.log(message);
	  }
	}
	
	class TimestampDecorator extends Logger {
	  constructor(logger) {
	    super();
	    this.logger = logger;
	  }
	
	  log(message) {
	    const timestampedMessage = `${new Date().toISOString()} - ${message}`;
	    this.logger.log(timestampedMessage);
	  }
	}
	
	class LevelDecorator extends Logger {
	  constructor(logger, level) {
	    super();
	    this.logger = logger;
	    this.level = level;
	  }
	
	  log(message) {
	    const leveledMessage = `[${this.level.toUpperCase()}] - ${message}`;
	    this.logger.log(leveledMessage);
	  }
	}
	
	// Usage
	const simpleLogger = new Logger();
	const timestampedLogger = new TimestampDecorator(simpleLogger);
	const leveledLogger = new LevelDecorator(timestampedLogger, 'info');
	
	leveledLogger.log('This message is logged with timestamp and info level.');In this example, the TimestampDecorator and LevelDecorator classes add additional functionalities (timestamping and leveling) to the Logger class dynamically. This pattern allows for flexible extension of the logger’s functionality.