Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

نمط الأمر

جزء من قسم البرمجة كائنية التوجه في رحلة Python على Coddy. الدرس 49 من 64.

يغلّف نمط Command الطلب في كائن، مما يتيح لك وضع العمليات في قائمة انتظار، وتسجيل الطلبات، ودعم وظيفة التراجع. ويفصل بين الكائن الذي يستدعي العملية والكائن الذي ينفّذها.

إليك أصناف command بسيطة:

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()

يُغلّف كل أمر عمليةً محددةً في كائن مستقبِل.

أنشئ receiver الذي ينفذ العمل الفعلي:

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

النقطة الأساسية: يحوّل نمط Command الطلبات إلى كائنات يمكن تخزينها وتمريرها وتنفيذها لاحقًا. لا يحتاج invoker إلى معرفة كيفية تنفيذ العملية - بل يستدعي execute() على كائن command. يتيح ذلك ميزات مثل التراجع/الإعادة، ووضع العمليات في قائمة انتظار، وتسجيل أوامر command.

challenge icon

التحدي

سهل

في هذا التحدي، ستنفّذ الفئة الأساسية التأسيسية Command في command.py: وهي لبنة البناء الأساسية لنمط Command. يركّز هذا التمرين تحديدًا على التغليف: تخزين البيانات بشكل خاص وإتاحة الوصول إليها بأمان من خلال خاصية.

عدّل command.py فقط وفقًا لتعليقات TODO. سترشدك تعليقات TODO إلى ما يلي:

  • تخزين اسم الأمر كسمة خاصة (_name)
  • إتاحته عبر خاصية للقراءة فقط (name)
  • تنفيذ أسلوب display_info() يطبع اسم الأمر

ملاحظة: يغطي هذا التحدي الفئة الأساسية Command فقط: أما Invoker وReceiver ووظيفة undo فسيتم تقديمها في دروس لاحقة. يمنحك إكمال هذه الخطوة أساس التغليف الذي يُبنى عليه النمط الكامل.

سيختبر 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 عبر الإنترنت