תבנית העיצוב
חלק מהיחידה תכנות מונחה עצמים במסלול ה-Python של Coddy. שיעור 51 מתוך 64.
תבנית העיצוב Decorator מוסיפה פונקציונליות חדשה לאובייקטים באופן דינמי, בלי לשנות את המבנה שלהם. היא עוטפת אובייקטים כדי להרחיב את ההתנהגות שלהם.
הנה דוגמה פשוטה לקפה:
class Coffee:
def cost(self):
return 5
def description(self):
return "Simple coffee"יוצרים מקשטים שמוסיפים תכונות לקפה:
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"כל מעטפת עוטפת אובייקט אחר ומוסיפה לו פונקציונליות משלה.
השתמשו במעטרים כדי להכין קפה בהתאמה אישית:
# מתחילים עם קפה בסיסי
my_coffee = Coffee()
print(f"{my_coffee.description()}: ${my_coffee.cost()}")
# מוסיפים חלב
my_coffee = MilkDecorator(my_coffee)
print(f"{my_coffee.description()}: ${my_coffee.cost()}")
# מוסיפים סוכר
my_coffee = SugarDecorator(my_coffee)
print(f"{my_coffee.description()}: ${my_coffee.cost()}")צרו דוגמה נוספת עם עיצוב טקסט:
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>"
# בנו טקסט מעוצב שלב אחר שלב
message = Text("Hello World")
message = BoldDecorator(message)
message = ItalicDecorator(message)
print(message.render())פלט:
Simple coffee: $5
Simple coffee + Milk: $7
Simple coffee + Milk + Sugar: $8
<i><b>Hello World</b></i>נקודה מרכזית: תבנית העיצוב Decorator עוטפת אובייקטים כדי להוסיף התנהגות חדשה בלי לשנות את האובייקט המקורי. כל מעטר שומר הפניה לאובייקט שאותו הוא עוטף ומוסיף פונקציונליות משלו. כך ניתן לשלב כמה מעטרים כדי ליצור שילובים גמישים של תכונות, בלי ליצור תתי־מחלקות רבות.
אתגר
בינוניבאתגר הזה תממש את דפוס העיצוב Decorator כדי ליצור מערכת גמישה להזמנת קפה בבית קפה. דפוס Decorator מאפשר להוסיף התנהגות לאובייקטים בודדים באופן דינמי, בלי להשפיע על ההתנהגות של אובייקטים אחרים מאותה מחלקה.
אתה בונה מערכת לבית קפה שצריכה:
- להציע סוגים שונים של משקאות (Espresso, Dark Roast, House Blend, Decaf)
- לאפשר ללקוחות להוסיף תוספות (Milk, Mocha, Soy, Whipped Cream) למשקאות שלהם
- לחשב את מחיר המשקאות עם התוספות שנוספו
- להשלים את המחלקה המופשטת
Beverageואת המימושים הקונקרטיים שלה בקובץbeverage.py - לממש את המחלקה המופשטת
CondimentDecoratorואת ה-decorators הקונקרטיים בקובץcondiment_decorator.py - לממש תרחישי בדיקה בקובץ
driver.pyכדי לאמת את המימוש שלך
נסו בעצמכם
# ייבוא כל המחלקות הדרושות
from beverage import Espresso, HouseBlend, DarkRoast, Decaf
from condiment_decorator import Milk, Mocha, Soy, Whip
# טיפול מקיף במקרי בדיקה
test_case = input()
if test_case == "basic_beverage_test":
# בדיקת הפונקציונליות הבסיסית של משקה
espresso = Espresso()
print(f"Description: {espresso.get_description()}")
print(f"Cost: ${espresso.cost():.2f}")
elif test_case == "all_beverages_test":
# בדיקת כל סוגי המשקאות
beverages = [Espresso(), HouseBlend(), DarkRoast(), Decaf()]
for beverage in beverages:
print(f"{beverage.get_description()}: ${beverage.cost():.2f}")
elif test_case == "single_condiment_test":
# בדיקת הוספת תוספת אחת למשקה
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":
# בדיקת הוספת כמה תוספות למשקה
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":
# בדיקת כל התוספות על משקה אחד
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":
# בדיקת חישובי עלות עבור שילובים שונים
espresso = Espresso()
# תוספות בודדות
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":
# בדיקה שסדר הוספת התוספות אינו משפיע על התוצאה הסופית
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":
# בדיקת הזמנות משקה מורכבות
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":
# בדיקת הוספה של אותה תוספת כמה פעמים
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":
# השווה את המחירים של משקאות שונים עם אותן תוספות
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":
# בדוק את המחיר של כל תוספת בנפרד
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":
# מצא את משקאות הבסיס הזול והיקר ביותר
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":
# בדוק עיטורים המקוננים זה בתוך זה לעומק
beverage = HouseBlend()
# הוסף 5 שכבות של עיטורים
decorated = Whip(Soy(Mocha(Milk(Whip(beverage)))))
print(f"Description: {decorated.get_description()}")
print(f"Cost: ${decorated.cost():.2f}")
# ספור את מספר התוספות
description = decorated.get_description()
condiment_count = description.count(" + ")
print(f"Number of condiments: {condiment_count}")
elif test_case == "cost_breakdown_test":
# פירוט מחירים מפורט
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":
# בדוק את העקביות בעיצוב התיאור
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}'")
# בדוק אם description מתחיל בשם משקה הבסיס
starts_correctly = description.startswith("Espresso")
print(f"Starts with base name: {starts_correctly}")
print("---")השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות התכנות מונחה העצמים (OOP)
קבצים חיצונייםמבוא ל-OOPמחלקות לעומת אובייקטיםהפרמטר selfמתודותמאפייניםשיטת הבנאי (__init__)חזרה: מחשבון פשוט5פולימורפיזם
בחינה מחדש של דריסת מתודותטיפוסיות ברווזמחלקות ומתודות אבסטרקטיותעיצוב ממשקיםסיכום – מחשבון צורות8מושגים מתקדמים בתכנות מונחה עצמים
קומפוזיציה לעומת הורשהMixinsמתודות סטטיות ומתודות מחלקהדקורטורים למחלקותמנהלי הקשרתרגלו בעצמכם: קומפיילר Python אונליין