CODESAMPLE

Decorator - JavaScript

Share on:

The Decorator pattern dynamically adds responsibilities to an object. It provides a flexible alternative to subclassing for extending functionality. Instead of inheriting from a base class, you “wrap” the original object with decorator objects, each adding a specific behavior. This implementation uses closures to encapsulate the decorated functionality, a common JavaScript approach. The decorate function takes a base object and a series of decorator functions, applying them sequentially to enhance the object’s behavior without modifying its core. This leverages JavaScript’s first-class function capabilities and avoids rigid class hierarchies.

/**
 * Decorator Pattern Implementation in JavaScript
 */

// Base Component
const coffee = {
  cost: () => 5,
  description: () => "Simple coffee"
};

// Decorator base
const decorate = (base, ...decorators) => {
  return decorators.reduce((acc, decorator) => decorator(acc), base);
};

// Concrete Decorators
const milkDecorator = (coffee) => {
  return {
    cost: () => coffee.cost() + 2,
    description: () => `Coffee with milk - ${coffee.description()}`
  };
};

const sugarDecorator = (coffee) => {
  return {
    cost: () => coffee.cost() + 1,
    description: () => `Coffee with sugar - ${coffee.description()}`
  };
};

const whippedCreamDecorator = (coffee) => {
    return {
        cost: () => coffee.cost() + 3,
        description: () => `Coffee with whipped cream - ${coffee.description()}`
    };
}

// Usage
const myCoffee = decorate(coffee, milkDecorator, sugarDecorator);
const fancyCoffee = decorate(coffee, milkDecorator, whippedCreamDecorator, sugarDecorator);

console.log(myCoffee.description()); // Output: Coffee with milk - Simple coffee
console.log(myCoffee.cost());       // Output: 7

console.log(fancyCoffee.description()); // Output: Coffee with whipped cream - Coffee with milk - Simple coffee
console.log(fancyCoffee.cost());       // Output: 10