Menu
Coddy logo textTech

Pattern Decorator

Fa parte della sezione Programmazione orientata agli oggetti del percorso Java di Coddy. Lezione 72 di 87.

Il Decorator Pattern è un pattern strutturale che ti permette di aggiungere nuovi comportamenti agli oggetti racchiudendoli 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.

Immagina una caffetteria in cui parti da un caffè semplice e puoi aggiungere extra come latte, zucchero o panna montata. Ogni aggiunta avvolge il caffè originale, aggiungendone il costo e la descrizione. Il pattern usa un'interfaccia comune, così gli oggetti decorati possono essere trattati come quello originale:

interface Coffee {
    String getDescription();
    double getCost();
}

class SimpleCoffee implements Coffee {
    public String getDescription() {
        return "Simple Coffee";
    }
    public double getCost() {
        return 2.0;
    }
}

abstract class CoffeeDecorator implements Coffee {
    protected Coffee coffee;
    
    public CoffeeDecorator(Coffee coffee) {
        this.coffee = coffee;
    }
}

class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee coffee) {
        super(coffee);
    }
    
    public String getDescription() {
        return coffee.getDescription() + ", Milk";
    }
    
    public double getCost() {
        return coffee.getCost() + 0.5;
    }
}

Il decoratore mantiene un riferimento all’oggetto avvolto e ne delega le chiamate, aggiungendo al contempo il proprio comportamento. Puoi impilare più decoratori per combinare le funzionalità:

Coffee order = new SimpleCoffee();
order = new MilkDecorator(order);
order = new SugarDecorator(order);

System.out.println(order.getDescription());  // Caffè semplice, latte, zucchero
System.out.println(order.getCost());         // 2.75

Il pattern Decorator dà il meglio di sé quando devi aggiungere responsabilità agli oggetti senza creare un’esplosione di sottoclassi. Ogni decoratore è indipendente, quindi puoi combinarli liberamente per creare esattamente la combinazione di comportamenti di cui hai bisogno.

challenge icon

Sfida

Facile

Costruiamo un sistema di notifiche usando il Decorator Pattern! Creerai una notifica di base che può essere arricchita con diversi canali di invio, avvisi via email, avvisi SMS e notifiche push, avvolgendo dei decorator l'uno attorno all'altro. Questo rispecchia i sistemi del mondo reale, in cui un singolo messaggio potrebbe dover essere inviato contemporaneamente attraverso più canali.

Organizzerai il codice in quattro file:

  • Notifier.java: Definisci l'interfaccia Notifier, che funge da interfaccia del componente per il tuo pattern decorator. Dovrebbe dichiarare un metodo send(String message) che gestisce l'invio delle notifiche.
  • BasicNotifier.java: Crea la classe concreta del componente che implementa Notifier. Rappresenta la forma più semplice di notifica: un avviso nell'app. Quando viene chiamato send, dovrebbe stampare In-App: [message].
  • NotifierDecorators.java: Qui costruirai la gerarchia dei decorator. Inizia con una classe astratta NotifierDecorator che implementa Notifier e contiene un riferimento a un oggetto Notifier avvolto (passato tramite il suo costruttore).

    Crea poi tre decorator concreti che estendono NotifierDecorator:

    EmailDecorator - Quando viene chiamato send, delega prima al notifier avvolto, poi stampa Email: [message].

    SMSDecorator - Quando viene chiamato send, delega prima al notifier avvolto, poi stampa SMS: [message].

    PushDecorator - Quando viene chiamato send, delega prima al notifier avvolto, poi stampa Push: [message].

  • Main.java: Metti insieme il tuo sistema di decorator! Riceverai due input: il messaggio di notifica (String) e un elenco separato da virgole dei canali da aggiungere (String), per esempio "email,sms" oppure "push,email,sms".

    Inizia con un BasicNotifier. Poi, in base all'input dei canali, avvolgilo con i decorator appropriati nell'ordine in cui compaiono. I nomi dei canali validi sono email, sms e push.

    Infine, chiama send con il tuo messaggio sul notifier completamente decorato.

Riceverai due input in quest'ordine: il messaggio da inviare (String) e i canali da attivare (String, separati da virgole).

Per esempio, con gli input Server is down! e email,push, l'output sarà:

In-App: Server is down!
Email: Server is down!
Push: Server is down!

Nota come ogni decorator aggiunga il proprio comportamento preservando la notifica originale. L'ordine di avvolgimento determina l'ordine dell'output: il componente più interno (BasicNotifier) viene eseguito per primo, poi ogni decorator aggiunge il proprio livello. Puoi combinare qualsiasi insieme di canali senza creare classi separate per ogni combinazione!

Provalo tu

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // Leggi il messaggio da inviare
        String message = scanner.nextLine();
        
        // Leggi l'elenco dei canali separati da virgole (ad es., "email,sms" o "push,email,sms")
        String channelsInput = scanner.nextLine();
        
        // TODO: Inizia con un BasicNotifier
        
        // TODO: Dividi l'input dei canali in corrispondenza delle virgole
        
        // TODO: Scorri ogni canale e avvolgi il notifier con il decorator appropriato
        // I canali validi sono: "email", "sms", "push"
        
        // TODO: Chiama send sul notifier completamente decorato, passando il messaggio
        
    }
}
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 Java online