Menu
Coddy logo textTech

Patrón Decorator

Parte de la sección Programación Orientada a Objetos del Journey de C# de Coddy — lección 58 de 70.

El patrón Decorator es un patrón estructural que te permite añadir nuevos comportamientos a objetos envolviéndolos en objetos especiales llamados decoradores. A diferencia de la herencia, que añade comportamiento en tiempo de compilación, los decoradores añaden funcionalidad dinámicamente en tiempo de ejecución sin modificar la clase original.

El patrón funciona haciendo que los decoradores implementen la misma interfaz que el objeto que envuelven. Cada decorador mantiene una referencia al objeto envuelto y le delega las llamadas, añadiendo su propio comportamiento antes o después:

public interface ICoffee
{
    string GetDescription();
    decimal GetCost();
}

public class SimpleCoffee : ICoffee
{
    public string GetDescription() => "Coffee";
    public decimal GetCost() => 2.00m;
}

public abstract class CoffeeDecorator : ICoffee
{
    protected ICoffee _coffee;
    
    public CoffeeDecorator(ICoffee coffee) => _coffee = coffee;
    
    public virtual string GetDescription() => _coffee.GetDescription();
    public virtual decimal GetCost() => _coffee.GetCost();
}

public class MilkDecorator : CoffeeDecorator
{
    public MilkDecorator(ICoffee coffee) : base(coffee) { }
    
    public override string GetDescription() => _coffee.GetDescription() + ", Milk";
    public override decimal GetCost() => _coffee.GetCost() + 0.50m;
}

El poder de los decoradores reside en su capacidad de apilarse. Puedes envolver un objeto decorado con otro decorador:

ICoffee coffee = new SimpleCoffee();
coffee = new MilkDecorator(coffee);
coffee = new MilkDecorator(coffee);  // ¡Doble leche!

Console.WriteLine(coffee.GetDescription());  // Café, Leche, Leche
Console.WriteLine(coffee.GetCost());         // 3.00

El patrón Decorator es ideal cuando necesitas añadir responsabilidades a los objetos sin crear una explosión de subclases. Cada decorador se centra en una única responsabilidad, y puedes combinarlos según sea necesario para construir el comportamiento exacto que deseas.

challenge icon

Desafío

Fácil

Vamos a construir un sistema de pedido de pizzas utilizando el patrón Decorator. Crearás una pizza base y luego añadirás ingredientes dinámicamente, donde cada ingrediente envuelve la capa anterior y se suma tanto a la descripción como al precio.

Organizarás tu código en tres archivos:

  • Pizza.cs: Define una interfaz IPizza en el espacio de nombres PizzaShop con dos métodos: GetDescription() que devuelve un string y GetPrice() que devuelve un decimal. Luego, crea una clase PlainPizza que implemente esta interfaz; debe devolver "Pizza" como su descripción y 8.00m como su precio base.
  • ToppingDecorators.cs: Crea una clase abstracta ToppingDecorator que implemente IPizza y mantenga una referencia a la pizza envuelta (pasada a través del constructor). Sus métodos virtuales deben delegar en la pizza envuelta. Luego, crea tres decoradores concretos:
    • CheeseTopping: añade ", Cheese" a la descripción y suma 1.50m al precio.
    • PepperoniTopping: añade ", Pepperoni" a la descripción y suma 2.00m al precio.
    • MushroomTopping: añade ", Mushrooms" a la descripción y suma 1.25m al precio.
  • Program.cs: Une todo comenzando con una pizza sencilla y envolviéndola con ingredientes basados en la entrada. Cada decorador de ingredientes envuelve la pizza anterior, construyendo el pedido final capa por capa.

Recibirás una entrada:

  • Una lista de ingredientes separados por comas para añadir (por ejemplo, cheese,pepperoni,cheese).

Comienza con una PlainPizza, luego envuélvela con el decorador apropiado para cada ingrediente en el orden en que aparecen. Los ingredientes válidos son cheese, pepperoni y mushrooms. Después de aplicar todos los ingredientes, imprime la descripción en una línea y el precio total en la siguiente línea.

Por ejemplo, si la entrada es cheese,pepperoni,mushrooms, la salida debería ser:

Pizza, Cheese, Pepperoni, Mushrooms
12.75

¡Observa cómo puedes añadir el mismo ingrediente varias veces! Si la entrada es cheese,cheese, obtendrías:

Pizza, Cheese, Cheese
11.00

Esto demuestra la flexibilidad del patrón decorator: cada decorador envuelve cualquier pizza que reciba, ya sea una pizza sencilla o una ya decorada, ¡permitiéndote apilar comportamientos dinámicamente!

Pruébalo tú mismo

using System;
using PizzaShop;

class Program
{
    public static void Main(string[] args)
    {
        // Lee la lista de ingredientes separados por comas
        string input = Console.ReadLine();
        string[] toppings = input.Split(',');
        
        // TODO: Comenzar con una PlainPizza
        
        // TODO: Recorrer cada ingrediente y envolver la pizza
        // con el decorador apropiado basado en el nombre del ingrediente:
        // - "cheese" -> CheeseTopping
        // - "pepperoni" -> PepperoniTopping
        // - "mushrooms" -> MushroomTopping
        
        // TODO: Imprimir la descripción final y el precio
    }
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos