Menu
Coddy logo textTech

Pattern Decorator

Fa parte della sezione Programmazione orientata agli oggetti del percorso GO di Coddy. Lezione 93 di 107.

Il pattern Decorator ti consente di aggiungere dinamicamente nuovi comportamenti agli oggetti avvolgendoli in altri oggetti. Mentre Adapter modifica un'interfaccia per adattarla a un'altra, Decorator mantiene la stessa interfaccia ma ne potenzia le funzionalità stratificando wrapper attorno all'oggetto originale.

In Go, lo implementiamo facendo implementare la stessa interfaccia sia al tipo di base sia ai decoratori:

type Notifier interface {
    Send(message string) string
}

type BasicNotifier struct{}

func (b BasicNotifier) Send(message string) string {
    return "Sending: " + message
}

I decoratori avvolgono un altro Notifier e aggiungono comportamento prima o dopo aver delegato a esso:

type TimestampDecorator struct {
    wrapped Notifier
}

func (t TimestampDecorator) Send(message string) string {
    timestamped := "[2024-01-15] " + message
    return t.wrapped.Send(timestamped)
}

type UppercaseDecorator struct {
    wrapped Notifier
}

func (u UppercaseDecorator) Send(message string) string {
    return strings.ToUpper(u.wrapped.Send(message))
}

La potenza del Decorator sta nella possibilità di impilare più wrapper per combinare i comportamenti:

notifier := BasicNotifier{}
withTimestamp := TimestampDecorator{wrapped: notifier}
withBoth := UppercaseDecorator{wrapped: withTimestamp}

fmt.Println(notifier.Send("Hello"))
// Sending: Hello

fmt.Println(withBoth.Send("Hello"))
// SENDING: [2024-01-15] HELLO

Decorator è ideale quando devi aggiungere responsabilità agli oggetti senza modificarne il codice, soprattutto quando servono diverse combinazioni di funzionalità. Viene comunemente usato per i livelli di logging, caching, autenticazione e compressione.

challenge icon

Sfida

Facile

Costruiamo un sistema per le ordinazioni di una caffetteria usando il pattern Decorator! Creerai una bevanda base a base di caffè e dei decorator che aggiungono extra come latte, zucchero e panna montata. Ogni decorator avvolge quello precedente, arricchendo sia la descrizione sia il prezzo: un perfetto esempio reale di come i decorator aggiungano funzionalità a strati.

Organizzerai il codice in tre file:

  • beverage.go: definisci l’interfaccia principale e il tipo base per il caffè.

    Crea un’interfaccia Beverage con due metodi:

    • Description() string: restituisce il tipo di bevanda
    • Cost() float64: restituisce il prezzo

    Implementa una struct Coffee come bevanda base. La sua descrizione dovrebbe essere Coffee e il suo costo dovrebbe essere 2.00.

  • decorators.go: crea i tipi decorator che aggiungono extra a qualsiasi bevanda.

    Ogni decorator avvolge un Beverage e implementa la stessa interfaccia. Crea tre decorator:

    • MilkDecorator: aggiunge , Milk alla descrizione e 0.50 al costo
    • SugarDecorator: aggiunge , Sugar alla descrizione e 0.25 al costo
    • WhipDecorator: aggiunge , Whip alla descrizione e 0.75 al costo

    Ogni decorator dovrebbe chiamare i metodi della bevanda avvolta e arricchirne il risultato.

  • main.go: crea bevande personalizzate impilando i decorator.

    Leggi il numero di extra da aggiungere. Poi, per ogni extra, leggine il tipo (milk, sugar o whip) e avvolgi la bevanda corrente con il decorator appropriato.

    Dopo aver applicato tutti gli extra, stampa la descrizione e il costo finali della bevanda su righe separate. Formatta il costo come $X.XX con esattamente due cifre decimali.

Verranno forniti i seguenti input:

  • Riga 1: numero di extra da aggiungere
  • Righe successive: un tipo di extra per riga (milk, sugar o whip)

Per esempio, dato:

2
milk
sugar

Il tuo output dovrebbe essere:

Coffee, Milk, Sugar
$2.75

E dato:

3
whip
milk
milk

Il tuo output dovrebbe essere:

Coffee, Whip, Milk, Milk
$3.75

E dato:

0

Il tuo output dovrebbe essere:

Coffee
$2.00

Nota come ogni decorator avvolge la bevanda precedente, arricchendo sia la descrizione sia il costo. Puoi aggiungere lo stesso extra più volte (come una doppia dose di latte) e l’ordine dei decorator determina l’ordine nella descrizione. Il caffè base non sa nulla degli extra: ogni decorator si limita ad arricchire ciò che avvolge!

Provalo tu

package main

import (
	"fmt"
)

func main() {
	// Leggi il numero di aggiunte
	var count int
	fmt.Scanln(&count)

	// Inizia con un caffè di base
	var drink Beverage = &Coffee{}

	// Leggi ogni aggiunta e avvolgi drink con il decoratore appropriato
	for i := 0; i < count; i++ {
		var extra string
		fmt.Scanln(&extra)

		// TODO: In base al tipo di aggiunta, avvolgi drink con il decoratore appropriato
		// Usa MilkDecorator per "milk"
		// Usa SugarDecorator per "sugar"
		// Usa WhipDecorator per "whip"
	}

	// TODO: Stampa la descrizione finale e il costo
	// Formatta il costo come $X.XX con esattamente due cifre decimali
}
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 Go online