Menu
Coddy logo textTech

Decorator-Muster

Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy. Lektion 93 von 107.

Das Dekorierer-Muster ermöglicht es dir, Objekten dynamisch neues Verhalten hinzuzufügen, indem du sie in andere Objekte einhüllst. Während der Adapter eine Schnittstelle so verändert, dass sie zu einer anderen passt, behält der Dekorierer dieselbe Schnittstelle bei, erweitert jedoch die Funktionalität, indem er Wrapper um das ursprüngliche Objekt schichtet.

In Go implementieren wir dies, indem sowohl der Basistyp als auch die Dekoratoren dieselbe Schnittstelle implementieren:

type Notifier interface {
    Send(message string) string
}

type BasicNotifier struct{}

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

Decoratoren umschließen einen weiteren Notifier und fügen vor oder nach der Delegation an ihn Verhalten hinzu:

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

Die Stärke des Decorator-Musters liegt darin, mehrere Wrapper zu stapeln, um Verhaltensweisen zu kombinieren:

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

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

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

Das Decorator-Muster ist ideal, wenn du Objekten Verantwortlichkeiten hinzufügen musst, ohne ihren Code zu ändern, insbesondere wenn verschiedene Kombinationen von Funktionen benötigt werden. Es wird häufig für Protokollierungs-, Caching-, Authentifizierungs- und Komprimierungsschichten verwendet.

challenge icon

Aufgabe

Einfach

Erstellen wir ein Bestellsystem für ein Café mit dem Decorator-Muster! Du erstellst ein Basis-Kaffeegetränk und Decorators, die Extras wie Milch, Zucker und Schlagsahne hinzufügen. Jeder Decorator umschließt den vorherigen und baut sowohl die Beschreibung als auch den Preis auf: ein perfektes Beispiel aus der realen Welt dafür, wie Decorators Funktionalität schichten.

Du organisierst deinen Code über drei Dateien:

  • beverage.go: Definiere deine zentrale Schnittstelle und den grundlegenden Kaffeetyp.

    Erstelle eine Beverage-Schnittstelle mit zwei Methoden:

    • Description() string: gibt an, um welches Getränk es sich handelt
    • Cost() float64: gibt den Preis zurück

    Implementiere eine Coffee-Struktur als dein Basisgetränk. Ihre Beschreibung sollte Coffee sein und ihre Kosten sollten 2.00 betragen.

  • decorators.go: Erstelle deine Decorator-Typen, die jedem Getränk Extras hinzufügen.

    Jeder Decorator umschließt ein Beverage und implementiert dieselbe Schnittstelle. Erstelle drei Decorators:

    • MilkDecorator: fügt der Beschreibung , Milk und den Kosten 0.50 hinzu
    • SugarDecorator: fügt der Beschreibung , Sugar und den Kosten 0.25 hinzu
    • WhipDecorator: fügt der Beschreibung , Whip und den Kosten 0.75 hinzu

    Jeder Decorator sollte die Methoden des umschlossenen Getränks aufrufen und das Ergebnis erweitern.

  • main.go: Erstelle individuelle Getränke, indem du Decorators stapelst.

    Lies die Anzahl der hinzuzufügenden Extras ein. Lies dann für jedes Extra seinen Typ (milk, sugar oder whip) ein und umschließe dein aktuelles Getränk mit dem passenden Decorator.

    Gib nach dem Anwenden aller Extras die Beschreibung und die Kosten des fertigen Getränks in separaten Zeilen aus. Formatiere die Kosten als $X.XX mit genau zwei Dezimalstellen.

Die folgenden Eingaben werden bereitgestellt:

  • Zeile 1: Anzahl der hinzuzufügenden Extras
  • Folgende Zeilen: Ein Extra-Typ pro Zeile (milk, sugar oder whip)

Zum Beispiel bei folgender Eingabe:

2
milk
sugar

sollte deine Ausgabe so aussehen:

Coffee, Milk, Sugar
$2.75

Und bei folgender Eingabe:

3
whip
milk
milk

sollte deine Ausgabe so aussehen:

Coffee, Whip, Milk, Milk
$3.75

Und bei folgender Eingabe:

0

sollte deine Ausgabe so aussehen:

Coffee
$2.00

Beachte, wie jeder Decorator das vorherige Getränk umschließt und dabei sowohl die Beschreibung als auch die Kosten aufbaut. Du kannst dasselbe Extra mehrmals hinzufügen (zum Beispiel doppelte Milch), und die Reihenfolge der Decorators bestimmt die Reihenfolge in der Beschreibung. Der Basiskaffee weiß nichts über die Extras: Jeder Decorator erweitert einfach das, was er umschließt!

Probier es selbst

package main

import (
	"fmt"
)

func main() {
	// Lies die Anzahl der Extras
	var count int
	fmt.Scanln(&count)

	// Beginne mit einem Basis-Kaffee
	var drink Beverage = &Coffee{}

	// Lies jedes Extra und umhülle das Getränk mit dem entsprechenden Decorator
	for i := 0; i < count; i++ {
		var extra string
		fmt.Scanln(&extra)

		// TODO: Basierend auf dem Extra-Typ, umhülle drink mit dem entsprechenden Decorator
		// Verwende MilkDecorator für "milk"
		// Verwende SugarDecorator für "sugar"
		// Verwende WhipDecorator für "whip"
	}

	// TODO: Gib die endgültige Beschreibung und die Kosten aus
	// Formatiere die Kosten als $X.XX mit genau zwei Dezimalstellen
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Online-Go-Compiler