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.00Il 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.
Sfida
FacileCostruiamo 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'interfacciaIPizzanello spazio dei nomiPizzaShopcon due metodi:GetDescription(), che restituisce una stringa, eGetPrice(), che restituisce un decimal. Poi crea una classePlainPizzache implementa questa interfaccia: dovrà restituire"Pizza"come descrizione e8.00mcome prezzo base.ToppingDecorators.cs: Crea una classe astrattaToppingDecoratorche implementaIPizzae 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 e1.50mal prezzoPepperoniTopping- aggiunge", Pepperoni"alla descrizione e2.00mal prezzoMushroomTopping- aggiunge", Mushrooms"alla descrizione e1.25mal 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.75Nota che puoi aggiungere più volte lo stesso condimento! Se l'input è cheese,cheese, otterrai:
Pizza, Cheese, Cheese
11.00Questo 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
}
}
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
1Fondamenti della programmazione orientata agli oggetti
File esterniNamespace e direttiveIntroduzione a classi e oggettiLa parola chiave "this"Metodi e parametriCampi e proprietàCostruttoriInizializzatori di oggettiRiepilogo - Calcolatrice semplice4Ereditarietà
Sintassi di base dell'ereditarietà (:)La parola chiave 'base'Parole chiave virtual e overrideClassi sealedLa classe base 'object'Riepilogo - Gerarchia dei dipendenti7Funzionalità avanzate
Sovraccarico degli operatoriIndicizzatori (this[])Override di ToString()Metodi di estensioneRiepilogo - Lista personalizzata10Pattern di progettazione Parte 1
Introduzione ai pattern di progettazioneSingleton thread-safePattern FactoryPattern Observer (eventi)Pattern Strategy2Proprietà e membri statici
Proprietà implementate automaticamenteProprietà di sola lettura e di sola scritturaCampi e metodi staticiClassi staticheMembri con corpo di espressione5Polimorfismo e interfacce
Polimorfismo a compile time e runtimeInterfaccia o classe astrattaInterfacce multipleInterfacce espliciteUpcasting e downcastingRipasso: calcolatore di forme8Concetti avanzati di programmazione a oggetti
Composizione invece dell'ereditarietàTipi generici (classi e metodi)Delegati ed eventiAttributi e reflectionIDisposable e istruzione usingNozioni di base sull'iniezione delle dipendenze11Pattern di progettazione, parte 2
Pattern del ComandoPattern dell’AdattatorePattern del DecoratorePattern Template MethodPattern dello StatoPattern Composito3Architettura delle classi
Dati di istanza vs staticiParole chiave 'readonly' e 'const'Campi di supportoRiepilogo - Gestore di conti bancari6Incapsulamento
Modificatori di accessoProprietà per l'incapsulamentoImplementazione dell'occultamento dei datiModelli di immutabilitàRiepilogo - Registri degli studentiEsercitati da solo: Compilatore C# online