Dekorierermuster
Teil des Abschnitts Objektorientierte Programmierung der Python-Journey von Coddy. Lektion 51 von 64.
Das Dekorator-Muster fügt Objekten dynamisch neue Funktionalität hinzu, ohne ihre Struktur zu verändern. Es umschließt Objekte, um ihr Verhalten zu erweitern.
Hier ist ein einfaches Kaffee-Beispiel:
class Coffee:
def cost(self):
return 5
def description(self):
return "Simple coffee"Erstellen Sie Dekoratoren, die dem Kaffee Funktionen hinzufügen:
class MilkDecorator:
def __init__(self, coffee):
self.coffee = coffee
def cost(self):
return self.coffee.cost() + 2
def description(self):
return self.coffee.description() + " + Milk"
class SugarDecorator:
def __init__(self, coffee):
self.coffee = coffee
def cost(self):
return self.coffee.cost() + 1
def description(self):
return self.coffee.description() + " + Sugar"Jeder Dekorierer umschließt ein anderes Objekt und fügt seine eigene Funktionalität hinzu.
Verwende Dekorierer, um individuell gestalteten Kaffee zuzubereiten:
# Beginne mit einfachem Kaffee
my_coffee = Coffee()
print(f"{my_coffee.description()}: ${my_coffee.cost()}")
# Milch hinzufügen
my_coffee = MilkDecorator(my_coffee)
print(f"{my_coffee.description()}: ${my_coffee.cost()}")
# Zucker hinzufügen
my_coffee = SugarDecorator(my_coffee)
print(f"{my_coffee.description()}: ${my_coffee.cost()}")Erstellen Sie ein weiteres Beispiel mit Textformatierung:
class Text:
def __init__(self, content):
self.content = content
def render(self):
return self.content
class BoldDecorator:
def __init__(self, text):
self.text = text
def render(self):
return f"<b>{self.text.render()}</b>"
class ItalicDecorator:
def __init__(self, text):
self.text = text
def render(self):
return f"<i>{self.text.render()}</i>"
# Formatierten Text Schritt für Schritt erstellen
message = Text("Hello World")
message = BoldDecorator(message)
message = ItalicDecorator(message)
print(message.render())Ausgabe:
Simple coffee: $5
Simple coffee + Milk: $7
Simple coffee + Milk + Sugar: $8
<i><b>Hello World</b></i>Wichtiger Punkt: Das Dekorator-Muster umschließt Objekte, um neues Verhalten hinzuzufügen, ohne das ursprüngliche Objekt zu ändern. Jeder Dekorator behält eine Referenz auf das umschlossene Objekt und fügt seine eigene Funktionalität hinzu. Dadurch kannst du mehrere Dekoratoren kombinieren und flexible Funktionskombinationen erstellen, ohne viele Unterklassen anzulegen.
Aufgabe
MittelIn dieser Herausforderung implementierst du das Decorator-Entwurfsmuster, um ein flexibles Bestellsystem für ein Café zu erstellen. Das Decorator-Muster ermöglicht es, einzelnen Objekten dynamisch Verhalten hinzuzufügen, ohne das Verhalten anderer Objekte derselben Klasse zu beeinflussen.
Du erstellst ein System für ein Café, das Folgendes können muss:
- Verschiedene Getränkearten anbieten (Espresso, Dark Roast, House Blend, Decaf)
- Kundinnen und Kunden ermöglichen, ihren Getränken Zutaten hinzuzufügen (Milk, Mocha, Soy, Whipped Cream)
- Die Kosten von Getränken mit hinzugefügten Zutaten berechnen
- Vervollständige die abstrakte Klasse
Beverageund ihre konkreten Implementierungen inbeverage.py - Implementiere die abstrakte Klasse
CondimentDecoratorund konkrete Decorators incondiment_decorator.py - Implementiere Testszenarien in
driver.py, um deine Implementierung zu überprüfen
Probier es selbst
# Importiere alle notwendigen Klassen
from beverage import Espresso, HouseBlend, DarkRoast, Decaf
from condiment_decorator import Milk, Mocha, Soy, Whip
# Umfassender Testfall-Handler
test_case = input()
if test_case == "basic_beverage_test":
# Teste grundlegende Getränke-Funktionalität
espresso = Espresso()
print(f"Description: {espresso.get_description()}")
print(f"Cost: ${espresso.cost():.2f}")
elif test_case == "all_beverages_test":
# Teste alle Getränketypen
beverages = [Espresso(), HouseBlend(), DarkRoast(), Decaf()]
for beverage in beverages:
print(f"{beverage.get_description()}: ${beverage.cost():.2f}")
elif test_case == "single_condiment_test":
# Teste Dekoration mit einer einzelnen Zutat
espresso = Espresso()
espresso_with_milk = Milk(espresso)
print(f"Description: {espresso_with_milk.get_description()}")
print(f"Cost: ${espresso_with_milk.cost():.2f}")
elif test_case == "multiple_condiments_test":
# Teste Dekoration mit mehreren Zutaten
house_blend = HouseBlend()
decorated_coffee = Mocha(Soy(Whip(house_blend)))
print(f"Description: {decorated_coffee.get_description()}")
print(f"Cost: ${decorated_coffee.cost():.2f}")
elif test_case == "all_condiments_test":
# Teste alle Zutaten auf einem Getränk
dark_roast = DarkRoast()
fully_loaded = Milk(Mocha(Soy(Whip(dark_roast))))
print(f"Description: {fully_loaded.get_description()}")
print(f"Cost: ${fully_loaded.cost():.2f}")
elif test_case == "cost_calculation_test":
# Teste Kostenberechnungen für verschiedene Kombinationen
espresso = Espresso()
# Einzelne Ergänzungen
with_milk = Milk(espresso)
with_mocha = Mocha(espresso)
print(f"Espresso: ${espresso.cost():.2f}")
print(f"Espresso + Milk: ${with_milk.cost():.2f}")
print(f"Espresso + Mocha: ${with_mocha.cost():.2f}")
elif test_case == "decoration_order_test":
# Teste, dass die Reihenfolge der Dekoration das Endergebnis nicht beeinflusst
decaf = Decaf()
order1 = Milk(Mocha(decaf))
order2 = Mocha(Milk(decaf))
print(f"Order 1 - {order1.get_description()}: ${order1.cost():.2f}")
print(f"Order 2 - {order2.get_description()}: ${order2.cost():.2f}")
print(f"Same cost: {order1.cost() == order2.cost()}")
elif test_case == "complex_order_test":
# Teste komplexe Getränkebestellungen
orders = [
Mocha(Whip(Espresso())),
Soy(Milk(HouseBlend())),
Whip(Mocha(Soy(DarkRoast()))),
Milk(Decaf())
]
for i, order in enumerate(orders, 1):
print(f"Order {i}: {order.get_description()}")
print(f"Cost: ${order.cost():.2f}")
print("---")
elif test_case == "double_condiment_test":
# Teste das mehrfache Hinzufügen derselben Zutat
espresso = Espresso()
double_mocha = Mocha(Mocha(espresso))
print(f"Description: {double_mocha.get_description()}")
print(f"Cost: ${double_mocha.cost():.2f}")
elif test_case == "beverage_comparison_test":
# Vergleiche Kosten verschiedener Getränke mit denselben Zutaten
base_beverages = [Espresso(), HouseBlend(), DarkRoast(), Decaf()]
print("All beverages with Milk + Mocha:")
for beverage in base_beverages:
decorated = Milk(Mocha(beverage))
print(f"{decorated.get_description()}: ${decorated.cost():.2f}")
elif test_case == "condiment_cost_test":
# Teste die Kosten einzelner Zutaten
espresso = Espresso()
base_cost = espresso.cost()
condiments = [
("Milk", Milk(espresso)),
("Mocha", Mocha(espresso)),
("Soy", Soy(espresso)),
("Whip", Whip(espresso))
]
for name, decorated in condiments:
additional_cost = decorated.cost() - base_cost
print(f"{name} adds: ${additional_cost:.2f}")
elif test_case == "cheapest_most_expensive_test":
# Finde das günstigste und das teuerste Basisgetränk
beverages = [
("Espresso", Espresso()),
("House Blend", HouseBlend()),
("Dark Roast", DarkRoast()),
("Decaf", Decaf())
]
beverages.sort(key=lambda x: x[1].cost())
print(f"Cheapest: {beverages[0][0]} - ${beverages[0][1].cost():.2f}")
print(f"Most Expensive: {beverages[-1][0]} - ${beverages[-1][1].cost():.2f}")
elif test_case == "nested_decoration_test":
# Teste tief verschachtelte Dekorationen
beverage = HouseBlend()
# Füge 5 Schichten von Dekorationen hinzu
decorated = Whip(Soy(Mocha(Milk(Whip(beverage)))))
print(f"Description: {decorated.get_description()}")
print(f"Cost: ${decorated.cost():.2f}")
# Zähle die Anzahl der Zutaten
description = decorated.get_description()
condiment_count = description.count(" + ")
print(f"Number of condiments: {condiment_count}")
elif test_case == "cost_breakdown_test":
# Detaillierte Kostenaufstellung
dark_roast = DarkRoast()
print(f"Base Dark Roast: ${dark_roast.cost():.2f}")
with_soy = Soy(dark_roast)
print(f"+ Soy: ${with_soy.cost():.2f}")
with_mocha = Mocha(with_soy)
print(f"+ Mocha: ${with_mocha.cost():.2f}")
with_whip = Whip(with_mocha)
print(f"+ Whip: ${with_whip.cost():.2f}")
final_cost = with_whip.cost()
print(f"Final total: ${final_cost:.2f}")
elif test_case == "description_format_test":
# Teste die Konsistenz der Formatierung der Beschreibung
espresso = Espresso()
test_combinations = [
Milk(espresso),
Mocha(Milk(espresso)),
Whip(Mocha(Milk(espresso))),
Soy(Whip(Mocha(Milk(espresso))))
]
for combo in test_combinations:
description = combo.get_description()
print(f"Description: '{description}'")
# Prüfe, ob die Beschreibung mit dem Namen des Basisgetränks beginnt
starts_correctly = description.startswith("Espresso")
print(f"Starts with base name: {starts_correctly}")
print("---")Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienEinführung in die OOPKlassen vs. ObjekteDer self-ParameterMethodenAttributeKonstruktormethode (__init__)Zusammenfassung – Einfacher Taschenrechner4Vererbung
Grundlegende VererbungDie super()-FunktionMethodenüberschreibungMehrfachvererbungMethodenauflösungsreihenfolgeWiederholung – Mitarbeiterhierarchie7Spezielle Methoden
Einführung in magische MethodenOperatorüberladungMagische Methoden für ContainerRückblick – Benutzerdefinierte Liste10Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFactory-MusterObserver-MusterStrategy-Muster13Abschließende Herausforderungen
E-Learning-PlattformBankensystemEntwicklung von SpielfigurenFahrzeugvermietungsservice2Dekoratoren
Einführung in DekoratorenProperty-DekoratorDekorator für statische MethodenDekorator für Klassenmethoden5Polymorphie
Methodenüberschreibung erneut betrachtetDuck-TypingAbstrakte Klassen und MethodenSchnittstellendesignRückblick – Formrechner8Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungMixinsStatische Methoden und KlassenmethodenKlassen-DekoratorenKontextmanager11Entwurfsmuster Teil 2
BefehlsmusterAdaptermusterDekorierermusterSchablonenmethodenmusterZustandsmusterKompositumsmuster3Klassenattribute
Instanz- vs. KlassenvariablenProperty-DecoratorsPrivate AttributeWiederholung – Bankkontoverwaltung6Kapselung
Öffentliche, geschützte, private MemberZugriffsmodifikatorenInformationsverbergungProperty-Decorators – FortgeschrittenWiederholung – System zur Verwaltung von Studentendaten12Projekt: Bibliotheksverwaltung
ProjektübersichtBuch- und BenutzerklassenAusleihsystemSuchfunktionAdministrationsoberflächeTests und IntegrationÜbe selbstständig: Online-Python-Compiler