Menu
Coddy logo textTech

תבנית הפקודה

חלק מהיחידה תכנות מונחה עצמים במסלול ה-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() על אובייקט הפקודה. כך מתאפשרות תכונות כמו ביטול/ביצוע מחדש, תור של פעולות ורישום פקודות.

challenge icon

אתגר

קל

באתגר הזה תממשו את מחלקת הבסיס היסודית 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)}")
quiz iconבחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה תכנות מונחה עצמים

תרגלו בעצמכם: קומפיילר Python אונליין