תבנית הפקודה
חלק מהיחידה תכנות מונחה עצמים במסלול ה-Python של Coddy. שיעור 49 מתוך 64.
תבנית הפקודה עוטפת בקשה כאובייקט, ומאפשרת לך לתזמן פעולות בתור, לתעד בקשות ולתמוך בפונקציונליות של ביטול. היא מפרידה בין האובייקט שקורא לפעולה לבין זה שמבצע אותה.
הנה מחלקות פקודה פשוטות:
class Command:
def execute(self):
pass
class LightOnCommand(Command):
def __init__(self, light):
self.light = light
def execute(self):
self.light.turn_on()
class LightOffCommand(Command):
def __init__(self, light):
self.light = light
def execute(self):
self.light.turn_off()כל פקודה עוטפת פעולה מסוימת על אובייקט מקבל.
יוצרים את המקלט שמבצע את העבודה בפועל:
class Light:
def turn_on(self):
print("Light is on")
def turn_off(self):
print("Light is off")צרו מפעיל שמבצע פקודות:
class RemoteControl:
def __init__(self):
self.command = None
def set_command(self, command):
self.command = command
def press_button(self):
self.command.execute()השתמש בתבנית הפקודה:
# יצירת מקבל הפקודות
light = Light()
# יצירת פקודות
light_on = LightOnCommand(light)
light_off = LightOffCommand(light)
# יצירת מפעיל הפקודות
remote = RemoteControl()
# ביצוע פקודות שונות
remote.set_command(light_on)
remote.press_button()
remote.set_command(light_off)
remote.press_button()הוסיפו תמיכה בפעולות ביטול:
class UndoableCommand(Command):
def undo(self):
pass
class LightOnCommand(UndoableCommand):
def __init__(self, light):
self.light = light
def execute(self):
self.light.turn_on()
def undo(self):
self.light.turn_off()
class SmartRemote:
def __init__(self):
self.last_command = None
def execute_command(self, command):
command.execute()
self.last_command = command
def undo(self):
if self.last_command:
self.last_command.undo()
smart_remote = SmartRemote()
smart_remote.execute_command(LightOnCommand(light))
smart_remote.undo() # מכבה את האורפלט:
Light is on
Light is off
Light is on
Light is offנקודה מרכזית: דפוס הפקודה הופך בקשות לאובייקטים שאפשר לאחסן, להעביר ולהפעיל מאוחר יותר. המפעיל לא צריך לדעת איך לבצע את הפעולה — הוא רק קורא ל־execute() על אובייקט הפקודה. כך מתאפשרות תכונות כמו ביטול/ביצוע מחדש, תור של פעולות ורישום פקודות.
אתגר
קלבאתגר הזה תממשו את מחלקת הבסיס היסודית Command בקובץ command.py: אבן הבניין החיונית של תבנית Command. התרגיל מתמקד במיוחד בכימוס: אחסון נתונים באופן פרטי וחשיפה בטוחה שלהם באמצעות מאפיין.
שנו רק את command.py בהתאם להערות TODO. הערות TODO ינחו אתכם:
- לאחסן את שם הפקודה כמאפיין פרטי (
_name) - לחשוף אותו באמצעות מאפיין לקריאה בלבד (
name) - לממש מתודה
display_info()שמדפיסה את שם הפקודה
הערה: האתגר הזה עוסק רק במחלקת הבסיס Command: ה־Invoker, ה־Receiver ופונקציונליות הביטול מוצגים בשיעורים מאוחרים יותר. השלמת השלב הזה מעניקה לכם את יסודות הכימוס שעליהם מתבססת התבנית המלאה.
המימוש שלכם ייבדק באמצעות driver.py, שיוודא:
- פונקציונליות בסיסית ותבנית הפלט
- מקרי קצה (קלטים ריקים, תווים מיוחדים, שמות ארוכים)
- הגנה על מאפיין לקריאה בלבד (ניסיון לבצע
obj.name = ...אמור להעלותAttributeError) - קיומו של המאפיין הפרטי
_name
נסו בעצמכם
# ייבוא המחלקה Command מתוך command.py
from command import Command
# מנגנון מקיף לטיפול במקרי בדיקה
test_case = input()
if test_case == "basic_test":
obj = Command("Test Name")
obj.display_info()
elif test_case == "validation_test":
obj = Command("Validation Test")
print(f"Name: {obj.name}")
elif test_case == "empty_name_test":
obj = Command("")
obj.display_info()
print(f"Empty name handled: {'Yes' if obj.name == '' else 'No'}")
elif test_case == "property_access_test":
obj = Command("Property Test")
original_name = obj.name
try:
# פעולה זו אמורה להיכשל מכיוון ש-name הוא מאפיין לקריאה בלבד
obj.name = "Modified Name"
print("Property protection failed")
except AttributeError:
print("Property protected successfully")
print(f"Name unchanged: {obj.name == original_name}")
elif test_case == "multiple_commands_test":
commands = [
Command("First Command"),
Command("Second Command"),
Command("Third Command")
]
for cmd in commands:
cmd.display_info()
elif test_case == "attribute_test":
obj = Command("Attribute Test")
has_private_name = hasattr(obj, "_name")
print(f"Has _name attribute: {has_private_name}")
elif test_case == "special_chars_test":
obj = Command("!@#$%^&*()_+{}[]|\\:;\"'<>,.?/")
obj.display_info()
elif test_case == "long_name_test":
long_name = "A" * 100
obj = Command(long_name)
obj.display_info()
print(f"Name length: {len(obj.name)}")השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות התכנות מונחה העצמים (OOP)
קבצים חיצונייםמבוא ל-OOPמחלקות לעומת אובייקטיםהפרמטר selfמתודותמאפייניםשיטת הבנאי (__init__)חזרה: מחשבון פשוט5פולימורפיזם
בחינה מחדש של דריסת מתודותטיפוסיות ברווזמחלקות ומתודות אבסטרקטיותעיצוב ממשקיםסיכום – מחשבון צורות8מושגים מתקדמים בתכנות מונחה עצמים
קומפוזיציה לעומת הורשהMixinsמתודות סטטיות ומתודות מחלקהדקורטורים למחלקותמנהלי הקשרתרגלו בעצמכם: קומפיילר Python אונליין