Decorador

El patrón Decorador (Decorator) es un patrón de diseño estructural que permite añadir dinámicamente funcionalidades a objetos en tiempo de ejecución sin alterar la estructura de las clases existentes. Se utiliza para extender las funcionalidades de los objetos de una manera flexible y reutilizable, implementando cada funcionalidad en clases decoradoras separadas.

Concepto del Patrón Decorador

Imagina que tienes una clase base, por ejemplo, una bebida, y quieres añadir opciones como leche, azúcar, chocolate, etc., sin modificar la clase base ni crear subclases para cada combinación posible. El patrón decorador permite esto añadiendo estas funcionalidades en tiempo de ejecución.

Ejemplo en TypeScript

Vamos a crear un ejemplo simple que demuestra cómo funciona el patrón decorador. En este ejemplo, consideraremos una aplicación que simula una cafetería con diferentes tipos de café y opciones adicionales como leche y azúcar.

// Componente base
interface Coffee {
  getCost(): number;
  getDescription(): string;
}

// Clase concreta base
class SimpleCoffee implements Coffee {
  getCost(): number {
    return 10;
  }

  getDescription(): string {
    return "Simple coffee";
  }
}

// Decorador base
abstract class CoffeeDecorator implements Coffee {
  protected decoratedCoffee: Coffee;

  constructor(coffee: Coffee) {
    this.decoratedCoffee = coffee;
  }

  getCost(): number {
    return this.decoratedCoffee.getCost();
  }

  getDescription(): string {
    return this.decoratedCoffee.getDescription();
  }
}

// Decorador concreto: Milk
class WithMilk extends CoffeeDecorator {
  constructor(coffee: Coffee) {
    super(coffee);
  }

  getCost(): number {
    return this.decoratedCoffee.getCost() + 2;
  }

  getDescription(): string {
    return this.decoratedCoffee.getDescription() + ", with milk";
  }
}

// Decorador concreto: Sugar
class WithSugar extends CoffeeDecorator {
  constructor(coffee: Coffee) {
    super(coffee);
  }

  getCost(): number {
    return this.decoratedCoffee.getCost() + 1;
  }

  getDescription(): string {
    return this.decoratedCoffee.getDescription() + ", with sugar";
  }
}

// Uso del decorador
const simpleCoffee = new SimpleCoffee();
console.log(simpleCoffee.getDescription() + " costs $" + simpleCoffee.getCost());

const milkCoffee = new WithMilk(simpleCoffee);
console.log(milkCoffee.getDescription() + " costs $" + milkCoffee.getCost());

const sugarMilkCoffee = new WithSugar(milkCoffee);
console.log(sugarMilkCoffee.getDescription() + " costs $" + sugarMilkCoffee.getCost());

Explicación

  1. SimpleCoffee es la implementación básica de la interfaz Coffee.

  2. CoffeeDecorator es una clase abstracta que implementa la interfaz Coffee y mantiene una referencia a un objeto Coffee. Todos los decoradores concretos extenderán esta clase.

  3. WithMilk y WithSugar son decoradores concretos que añaden funcionalidades y costos adicionales al objeto Coffee.

  4. Al final, demostramos cómo se puede envolver un café simple con múltiples decoradores para agregar leche y azúcar, mostrando cómo el patrón Decorador permite añadir dinámicamente funcionalidades.

Este patrón es muy útil para respetar el principio de responsabilidad única y el principio de abierto/cerrado, permitiendo que las clases estén abiertas para la extensión pero cerradas para la modificación.

Last updated