Patrón Decorator en C#: Guía Completa con Ejemplos

Introducción: Capas que suman valor

Decorator permite añadir comportamiento a objetos sin modificar su clase original. Es ideal cuando no quieres crear una explosión de subclases por cada combinación de funcionalidades.

Capas superpuestas
Prompt: Layered translucent materials stacked, soft light, abstract minimal design.

1. Naturaleza: Capas protectoras

Las cebollas tienen capas. Cada capa añade protección sin cambiar la esencia de la cebolla. Eso es Decorator: añadir responsabilidad sin alterar el núcleo.

Cebolla en capas
Prompt: Onion cross-section showing layers, macro photography, high detail.

2. Mundo Real: Cafés con extras

Un café puede ser base, pero puedes añadir leche, canela o caramelo. Cada extra es un decorator que suma costo y descripción.

Café con toppings
Prompt: Latte with cinnamon and caramel drizzle, cozy cafe lighting, shallow depth of field.

3. Implementación en C#: Código Paso a Paso

3.1 Componente base

public interface ICafe
{
    string ObtenerDescripcion();
    decimal ObtenerCosto();
}

3.2 Componente concreto

public class CafeSimple : ICafe
{
    public string ObtenerDescripcion() => "Café simple";
    public decimal ObtenerCosto() => 1.50m;
}

3.3 Decorator base

public abstract class CafeDecorator : ICafe
{
    protected ICafe _cafe;
    protected CafeDecorator(ICafe cafe) { _cafe = cafe; }
    public virtual string ObtenerDescripcion() => _cafe.ObtenerDescripcion();
    public virtual decimal ObtenerCosto() => _cafe.ObtenerCosto();
}

3.4 Decorators concretos

public class ConLeche : CafeDecorator
{
    public ConLeche(ICafe cafe) : base(cafe) { }
    public override string ObtenerDescripcion() => base.ObtenerDescripcion() + ", con leche";
    public override decimal ObtenerCosto() => base.ObtenerCosto() + 0.50m;
}

public class ConCanela : CafeDecorator
{
    public ConCanela(ICafe cafe) : base(cafe) { }
    public override string ObtenerDescripcion() => base.ObtenerDescripcion() + ", con canela";
    public override decimal ObtenerCosto() => base.ObtenerCosto() + 0.30m;
}

3.5 Uso

ICafe miCafe = new CafeSimple();
miCafe = new ConLeche(miCafe);
miCafe = new ConCanela(miCafe);

Console.WriteLine(miCafe.ObtenerDescripcion());
Console.WriteLine($"Costo: {miCafe.ObtenerCosto()} €");

4. Decorator vs Composite

Composite compone estructuras jerárquicas. Decorator añade funcionalidades de forma dinámica. Ambos usan composición, pero con intenciones distintas.

5. Diagrama UML

UML Decorator
Prompt: UML diagram of Decorator pattern, clean vector, labels in Spanish.
Ejemplo de objetos decorados
Prompt: Stack of layered object icons showing decoration, modern infographic style.

⚠️ Cuándo NO Usar Decorator

  • Si las combinaciones posibles son muy pocas, una clase simple puede ser suficiente.
  • Si necesitas modificar el comportamiento interno de forma profunda.

💪 Ejercicio

Crea un sistema de notificaciones donde puedas añadir decoradores: ConEmail, ConSMS, ConSlack.

Conclusión

Decorator es la forma elegante de sumar funcionalidades sin herencia explosiva. Piensa en capas.