Menu
Coddy logo textTech

Pattern del Decoratore

Fa parte della sezione Programmazione orientata agli oggetti del percorso C# di Coddy. Lezione 58 di 70.

Il pattern Decorator è un pattern strutturale che ti permette di aggiungere nuovi comportamenti agli oggetti avvolgendoli in oggetti speciali chiamati decoratori. A differenza dell'ereditarietà, che aggiunge comportamenti in fase di compilazione, i decoratori aggiungono funzionalità dinamicamente in fase di esecuzione senza modificare la classe originale.

Il pattern funziona facendo sì che i decoratori implementino la stessa interfaccia dell'oggetto che avvolgono. Ogni decoratore mantiene un riferimento all'oggetto avvolto e ne delega le chiamate, aggiungendo il proprio comportamento prima o dopo:

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;
}

La potenza dei decoratori sta nella loro capacità di sovrapporsi. Puoi avvolgere un oggetto decorato con un altro decoratore:

ICoffee coffee = new SimpleCoffee();
coffee = new MilkDecorator(coffee);
coffee = new MilkDecorator(coffee);  // Doppio latte!

Console.WriteLine(coffee.GetDescription());  // Caffè, latte, latte
Console.WriteLine(coffee.GetCost());         // 3.00

Il pattern Decorator è ideale quando devi aggiungere responsabilità agli oggetti senza creare un’esplosione di sottoclassi. Ogni decoratore si concentra su un singolo aspetto e puoi combinarli secondo necessità per ottenere esattamente il comportamento che desideri.

challenge icon

Sfida

Facile

Costruiamo un sistema per ordinare pizze usando il pattern Decorator. Creerai una pizza base e poi vi aggiungerai dinamicamente i condimenti: ogni condimento avvolgerà il livello precedente, aggiungendo dettagli sia alla descrizione sia al prezzo.

Organizzerai il codice in tre file:

  • Pizza.cs: Definisci un'interfaccia IPizza nello spazio dei nomi PizzaShop con due metodi: GetDescription(), che restituisce una stringa, e GetPrice(), che restituisce un decimal. Poi crea una classe PlainPizza che implementa questa interfaccia: dovrà restituire "Pizza" come descrizione e 8.00m come prezzo base.
  • ToppingDecorators.cs: Crea una classe astratta ToppingDecorator che implementa IPizza e mantiene un riferimento alla pizza avvolta (passato tramite il costruttore). I suoi metodi virtuali dovranno delegare le chiamate alla pizza avvolta. Poi crea tre decoratori concreti:
    • CheeseTopping - aggiunge ", Cheese" alla descrizione e 1.50m al prezzo
    • PepperoniTopping - aggiunge ", Pepperoni" alla descrizione e 2.00m al prezzo
    • MushroomTopping - aggiunge ", Mushrooms" alla descrizione e 1.25m al prezzo
  • Program.cs: Metti insieme tutti gli elementi partendo da una pizza semplice e avvolgendola con i condimenti indicati nell'input. Ogni decoratore di condimento avvolge la pizza precedente, costruendo l'ordine finale livello dopo livello.

Riceverai un input:

  • Un elenco di condimenti separati da virgole da aggiungere (ad es., cheese,pepperoni,cheese)

Inizia con una PlainPizza, poi avvolgila con il decoratore appropriato per ogni condimento, nell'ordine in cui compaiono. I condimenti validi sono cheese, pepperoni e mushrooms. Dopo aver applicato tutti i condimenti, stampa la descrizione su una riga e il prezzo totale sulla riga successiva.

Per esempio, se l'input è cheese,pepperoni,mushrooms, l'output dovrebbe essere:

Pizza, Cheese, Pepperoni, Mushrooms
12.75

Nota che puoi aggiungere più volte lo stesso condimento! Se l'input è cheese,cheese, otterrai:

Pizza, Cheese, Cheese
11.00

Questo dimostra la flessibilità del pattern Decorator: ogni decoratore avvolge la pizza che riceve, che sia una pizza semplice o una già decorata, permettendoti di aggiungere comportamenti dinamicamente!

Provalo tu

using System;
using PizzaShop;

class Program
{
    public static void Main(string[] args)
    {
        // Leggi l'elenco dei condimenti separati da virgole
        string input = Console.ReadLine();
        string[] toppings = input.Split(',');
        
        // TODO: Inizia con una PlainPizza
        
        // TODO: Scorri ogni condimento e avvolgi la pizza
        // con il decoratore appropriato in base al nome del condimento:
        // - "cheese" -> CheeseTopping
        // - "pepperoni" -> PepperoniTopping
        // - "mushrooms" -> MushroomTopping
        
        // TODO: Stampa la descrizione e il prezzo finali
    }
}
quiz iconMettiti alla prova

Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.

Tutte le lezioni di Programmazione orientata agli oggetti

Esercitati da solo: Compilatore C# online