Menu
Coddy logo textTech

Dekorierermuster

Teil des Abschnitts Objektorientierte Programmierung der C#-Journey von Coddy. Lektion 58 von 70.

Das Dekorator-Muster ist ein strukturelles Muster, mit dem du Objekten neue Verhaltensweisen hinzufügen kannst, indem du sie in spezielle Objekte einhüllst, die als Dekoratoren bezeichnet werden. Im Gegensatz zur Vererbung, die Verhalten zur Kompilierzeit hinzufügt, erweitern Dekoratoren die Funktionalität dynamisch zur Laufzeit, ohne die ursprüngliche Klasse zu verändern.

Das Muster funktioniert, indem Dekorierer dieselbe Schnittstelle wie das Objekt implementieren, das sie umschließen. Jeder Dekorierer enthält eine Referenz auf das umschlossene Objekt und delegiert Aufrufe an dieses, wobei er davor oder danach sein eigenes Verhalten hinzufügt:

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

Die Stärke von Dekoratoren liegt in ihrer Fähigkeit, gestapelt zu werden. Sie können ein dekoriertes Objekt mit einem weiteren Dekorator umhüllen:

ICoffee coffee = new SimpleCoffee();
coffee = new MilkDecorator(coffee);
coffee = new MilkDecorator(coffee);  // Doppelte Milch!

Console.WriteLine(coffee.GetDescription());  // Kaffee, Milch, Milch
Console.WriteLine(coffee.GetCost());         // 3.00

Das Decorator-Muster ist ideal, wenn du Objekten Verantwortlichkeiten hinzufügen musst, ohne eine Flut von Unterklassen zu erzeugen. Jeder Decorator konzentriert sich auf einen einzelnen Aspekt, und du kombinierst sie nach Bedarf, um genau das gewünschte Verhalten zu erstellen.

challenge icon

Aufgabe

Einfach

Erstellen wir ein Pizza-Bestellsystem mithilfe des Decorator-Musters. Du erstellst eine Basispizza und fügst ihr dann dynamisch Beläge hinzu, wobei jeder Belag die vorherige Schicht umschließt und sowohl die Beschreibung als auch den Preis erweitert.

Du organisierst deinen Code auf drei Dateien verteilt:

  • Pizza.cs: Definiere eine IPizza-Schnittstelle im Namespace PizzaShop mit zwei Methoden: GetDescription(), die eine Zeichenkette zurückgibt, und GetPrice(), die eine Dezimalzahl zurückgibt. Erstelle anschließend eine PlainPizza-Klasse, die diese Schnittstelle implementiert – sie soll "Pizza" als Beschreibung und 8.00m als Grundpreis zurückgeben.
  • ToppingDecorators.cs: Erstelle eine abstrakte ToppingDecorator-Klasse, die IPizza implementiert und eine Referenz auf die umschlossene Pizza enthält (über den Konstruktor übergeben). Ihre virtuellen Methoden sollen die Aufrufe an die umschlossene Pizza weiterleiten. Erstelle anschließend drei konkrete Decorators:
    • CheeseTopping – hängt ", Cheese" an die Beschreibung an und addiert 1.50m zum Preis
    • PepperoniTopping – hängt ", Pepperoni" an die Beschreibung an und addiert 2.00m zum Preis
    • MushroomTopping – hängt ", Mushrooms" an die Beschreibung an und addiert 1.25m zum Preis
  • Program.cs: Führe alles zusammen, indem du mit einer einfachen Pizza beginnst und sie abhängig von der Eingabe mit Belägen umschließt. Jeder Belag-Decorator umschließt die vorherige Pizza, sodass die endgültige Bestellung Schicht für Schicht aufgebaut wird.

Du erhältst eine Eingabe:

  • Eine durch Kommas getrennte Liste der hinzuzufügenden Beläge (z. B. cheese,pepperoni,cheese)

Beginne mit einer PlainPizza und umschließe sie anschließend für jeden Belag in der Reihenfolge, in der die Beläge erscheinen, mit dem passenden Decorator. Gültige Beläge sind cheese, pepperoni und mushrooms. Gib nach dem Anwenden aller Beläge die Beschreibung in einer Zeile und den Gesamtpreis in der nächsten Zeile aus.

Wenn die Eingabe beispielsweise cheese,pepperoni,mushrooms lautet, sollte die Ausgabe so aussehen:

Pizza, Cheese, Pepperoni, Mushrooms
12.75

Beachte, dass du denselben Belag mehrmals hinzufügen kannst! Wenn die Eingabe cheese,cheese lautet, erhältst du:

Pizza, Cheese, Cheese
11.00

Dies veranschaulicht die Flexibilität des Decorator-Musters: Jeder Decorator umschließt die Pizza, die er erhält – unabhängig davon, ob es sich um eine einfache Pizza oder eine bereits dekorierte Pizza handelt. Dadurch kannst du Verhaltensweisen dynamisch stapeln!

Probier es selbst

using System;
using PizzaShop;

class Program
{
    public static void Main(string[] args)
    {
        // Lies die kommagetrennte Liste der Beläge ein
        string input = Console.ReadLine();
        string[] toppings = input.Split(',');
        
        // TODO: Beginne mit einer PlainPizza
        
        // TODO: Durchlaufe jeden Belag und umhülle die Pizza
        // mit dem passenden Decorator basierend auf dem Namen des Belags:
        // - "cheese" -> CheeseTopping
        // - "pepperoni" -> PepperoniTopping
        // - "mushrooms" -> MushroomTopping
        
        // TODO: Gib die endgültige Beschreibung und den Preis aus
    }
}
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-C#-Compiler