Menu
Coddy logo textTech

Patron Décorateur

Fait partie de la section Programmation Orientée Objet du Journey GO de Coddy — leçon 93 sur 107.

Le patron Décorateur vous permet d'ajouter dynamiquement de nouveaux comportements à des objets en les enveloppant dans d'autres objets. Alors que l'Adaptateur modifie une interface pour qu'elle corresponde à une autre, le Décorateur conserve la même interface mais améliore les fonctionnalités en superposant des enveloppes autour de l'objet original.

En Go, nous implémentons cela en faisant en sorte que le type de base et les décorateurs implémentent la même interface :

type Notifier interface {
    Send(message string) string
}

type BasicNotifier struct{}

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

Les décorateurs enveloppent un autre Notifier et ajoutent un comportement avant ou après lui avoir délégué la tâche :

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 puissance du Décorateur réside dans l'empilement de plusieurs wrappers pour combiner les comportements :

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

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

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

Le Décorateur est idéal lorsque vous devez ajouter des responsabilités à des objets sans modifier leur code, particulièrement lorsque différentes combinaisons de fonctionnalités sont nécessaires. Il est couramment utilisé pour les couches de journalisation, de mise en cache, d'authentification et de compression.

challenge icon

Défi

Facile

Construisons un système de commande de café en utilisant le motif de conception Decorator ! Vous allez créer une boisson au café de base et des décorateurs qui ajoutent des suppléments comme du lait, du sucre et de la crème fouettée. Chaque décorateur enveloppe le précédent, construisant à la fois la description et le prix — un exemple concret parfait de la façon dont les décorateurs superposent les fonctionnalités.

Vous organiserez votre code sur trois fichiers :

  • beverage.go : Définissez votre interface principale et le type de café de base.

    Créez une interface Beverage avec deux méthodes :

    • Description() string — retourne ce qu'est la boisson
    • Cost() float64 — retourne le prix

    Implémentez une structure Coffee comme boisson de base. Sa description doit être Coffee et son coût doit être 2.00.

  • decorators.go : Créez vos types de décorateurs qui ajoutent des suppléments à n'importe quelle boisson.

    Chaque décorateur enveloppe une Beverage et implémente la même interface. Construisez trois décorateurs :

    • MilkDecorator — ajoute , Milk à la description et 0.50 au coût
    • SugarDecorator — ajoute , Sugar à la description et 0.25 au coût
    • WhipDecorator — ajoute , Whip à la description et 0.75 au coût

    Chaque décorateur doit appeler les méthodes de la boisson enveloppée et améliorer le résultat.

  • main.go : Construisez des boissons personnalisées en empilant les décorateurs.

    Lisez un nombre de suppléments à ajouter. Ensuite, pour chaque supplément, lisez son type (milk, sugar, ou whip) et enveloppez votre boisson actuelle avec le décorateur approprié.

    Après avoir appliqué tous les suppléments, affichez la description et le coût final de la boisson sur des lignes séparées. Formatez le coût comme $X.XX avec exactement deux décimales.

Les entrées suivantes seront fournies :

  • Ligne 1 : Nombre de suppléments à ajouter
  • Lignes suivantes : Un type de supplément par ligne (milk, sugar, ou whip)

Par exemple, avec :

2
milk
sugar

Votre sortie devrait être :

Coffee, Milk, Sugar
$2.75

Et avec :

3
whip
milk
milk

Votre sortie devrait être :

Coffee, Whip, Milk, Milk
$3.75

Et avec :

0

Votre sortie devrait être :

Coffee
$2.00

Remarquez comment chaque décorateur enveloppe la boisson précédente, construisant à la fois la description et le coût. Vous pouvez ajouter le même supplément plusieurs fois (comme un double lait), et l'ordre des décorateurs détermine l'ordre dans la description. Le café de base ne sait rien des suppléments — chaque décorateur améliore simplement ce qu'il enveloppe !

Aide-mémoire

Le patron Décorateur ajoute dynamiquement de nouveaux comportements aux objets en les enveloppant dans d'autres objets. Il conserve la même interface tout en améliorant les fonctionnalités par superposition.

Le type de base et les décorateurs implémentent tous deux la même interface :

type Notifier interface {
    Send(message string) string
}

type BasicNotifier struct{}

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

Les décorateurs enveloppent un autre objet et ajoutent un comportement avant ou après la délégation :

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

Empilez plusieurs décorateurs pour combiner les comportements :

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

Utilisez le Décorateur pour ajouter des responsabilités sans modifier le code de l'objet, en particulier pour différentes combinaisons de fonctionnalités. Utilisations courantes : journalisation (logging), mise en cache, authentification, compression.

Essayez vous-même

package main

import (
	"fmt"
)

func main() {
	// Lire le nombre de suppléments
	var count int
	fmt.Scanln(&count)

	// Commencer avec un café de base
	var drink Beverage = &Coffee{}

	// Lire chaque supplément et envelopper la boisson avec le décorateur approprié
	for i := 0; i < count; i++ {
		var extra string
		fmt.Scanln(&extra)

		// TODO: Selon le type de supplément, envelopper drink avec le décorateur approprié
		// Utiliser MilkDecorator pour "milk"
		// Utiliser SugarDecorator pour "sugar"
		// Utiliser WhipDecorator pour "whip"
	}

	// TODO: Afficher la description finale et le coût
	// Formater le coût comme $X.XX avec exactement deux décimales
}
quiz iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Programmation Orientée Objet