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

نمط الأمر

جزء من قسم Object Oriented Programming في رحلة PHP على Coddy — الدرس 75 من 91.

يُعد Command Pattern (نمط الأمر) نمط تصميم سلوكي يحول الطلب إلى كائن مستقل بذاته يحتوي على كافة المعلومات المتعلقة بالطلب. يتيح لك هذا التحويل تمرير الطلبات كوسيطات للدوال (method arguments)، وتأجيل تنفيذ الطلب أو وضعه في قائمة انتظار، ودعم العمليات القابلة للتراجع.

فكر في مطعم.

عندما تطلب الطعام، يكتب النادل طلبك على ورقة (الأمر)، ويأخذها إلى المطبخ، ويقوم الطاهي بتنفيذه. لا يحتاج النادل إلى معرفة كيفية الطهي؛ فهم يقومون فقط بتوصيل الأمر.

هذا يفصل بين الطالب والمنفذ.

إليك تنفيذ أساسي:

<?php
interface Command {
    public function execute(): string;
}
class Light {
    public function on(): string { return "Light is ON"; }
    public function off(): string { return "Light is OFF"; }
}
class LightOnCommand implements Command {
    public function __construct(private Light $light) {}
    
    public function execute(): string {
        return $this->light->on();
    }
}
class LightOffCommand implements Command {
    public function __construct(private Light $light) {}
    
    public function execute(): string {
        return $this->light->off();
    }
}

يقوم المستدعي (invoker) بتشغيل الأوامر دون معرفة ما تفعله:

<?php
class RemoteControl {
    public function press(Command $command): string {
        return $command->execute();
    }
}
$light = new Light();
$remote = new RemoteControl();

echo $remote->press(new LightOnCommand($light)) . "\n";
echo $remote->press(new LightOffCommand($light));

المخرجات:

Light is ON
Light is OFF

يتفوق نمط الأمر (Command Pattern) عندما تحتاج إلى وضع العمليات في طابور، أو تنفيذ خاصية التراجع، أو تسجيل الإجراءات. كل أمر يكون قائماً بذاته، مما يسهل إضافة أوامر جديدة دون تعديل الكود الحالي.

challenge icon

التحدي

سهل

لنقم ببناء نظام أوامر لمحرر نصوص باستخدام نمط الأمر (Command Pattern). غالباً ما تحتاج محررات النصوص إلى دعم عمليات مثل كتابة النص، وحذف النص، والتراجع عن الإجراءات — حيث يصبح كل إجراء كائناً مستقلاً (command object) يمكن تنفيذه من خلال واجهة موحدة.

ستقوم بتنظيم الكود الخاص بك عبر أربعة ملفات:

  • Command.php — قم بتعريف واجهة Command مع طريقة واحدة execute(): string. يحدد هذا العقد الذي يجب أن تتبعه جميع أوامر المحرر.
  • TextEditor.php — أنشئ فئة TextEditor التي تمثل المستقبل (receiver) — الكائن الذي يقوم بالعمل الفعلي. يجب أن يقوم المحرر الخاص بك بـ:
    • تخزين محتوى النص الحالي (ابدأ بسلسلة نصية فارغة)
    • امتلاك طريقة write(string $text): string تقوم بإلحاق النص بالمحتوى وتعيد "Written: [text]"
    • امتلاك طريقة clear(): string تقوم بتفريغ المحتوى وتعيد "Content cleared"
    • امتلاك طريقة getContent(): string تعيد المحتوى الحالي
  • Commands.php — قم بتضمين ملفات Command و TextEditor. أنشئ فئتين للأوامر تنفذان واجهة Command:
    • WriteCommand — تأخذ TextEditor وسلسلة نصية في منشئها (constructor). تقوم طريقة execute() الخاصة بها باستدعاء طريقة write() للمحرر وتعيد النتيجة.
    • ClearCommand — تأخذ TextEditor في منشئها. تقوم طريقة execute() الخاصة بها باستدعاء طريقة clear() للمحرر وتعيد النتيجة.
  • main.php — قم بتضمين ملف Commands. ستتلقى مدخلين: نوع الأمر ("write" أو "clear") وسلسلة نصية (تُستخدم فقط لأوامر الكتابة).

    أنشئ مثيلاً من TextEditor. بناءً على نوع الأمر، أنشئ كائن الأمر المناسب. ثم أنشئ وظيفة استدعاء (invoker) أو آلية بسيطة تستدعي execute() على الأمر وتطبع النتيجة.

    في سطر جديد، اطبع محتوى المحرر الحالي بهذا التنسيق: Content: [content] (إذا كان فارغاً، اطبع فقط Content: مع عدم وجود شيء بعد المسافة).

يوضح هذا التحدي كيف يقوم نمط الأمر بتغليف كل عملية ككائن. لا يحتاج المستدعي (invoker) إلى معرفة ما إذا كان ينفذ أمر كتابة أو مسح — فهو ببساطة يستدعي execute() ويترك الأمر يتعامل مع التفاصيل.

ورقة مرجعية

يُعد نمط الأمر (Command Pattern) نمط تصميم سلوكي يغلف الطلب ككائن مستقل يحتوي على جميع المعلومات المتعلقة بالطلب. يتيح لك ذلك تمرير الطلبات كوسيطات للدوال، وجدولة العمليات، ودعم الإجراءات القابلة للتراجع.

يفصل هذا النمط بين طالب الطلب (المُستدعي/invoker) والمنفذ (المستقبل/receiver)، تماماً كما يقوم النادل بتوصيل الطلبات إلى الطاهي دون الحاجة إلى معرفة كيفية الطهي.

الهيكل الأساسي

قم بتعريف واجهة Command تحتوي على دالة execute():

interface Command {
    public function execute(): string;
}

قم بإنشاء فئة مستقبل (receiver class) تقوم بالعمل الفعلي:

class Light {
    public function on(): string { return "Light is ON"; }
    public function off(): string { return "Light is OFF"; }
}

قم بتنفيذ فئات أوامر ملموسة (concrete command classes) تغلف إجراءات محددة:

class LightOnCommand implements Command {
    public function __construct(private Light $light) {}
    
    public function execute(): string {
        return $this->light->on();
    }
}

class LightOffCommand implements Command {
    public function __construct(private Light $light) {}
    
    public function execute(): string {
        return $this->light->off();
    }
}

المُستدعي (Invoker)

يقوم المُستدعي بتشغيل الأوامر دون معرفة تفاصيل تنفيذها:

class RemoteControl {
    public function press(Command $command): string {
        return $command->execute();
    }
}

$light = new Light();
$remote = new RemoteControl();

echo $remote->press(new LightOnCommand($light)) . "\n";
echo $remote->press(new LightOffCommand($light));

الفوائد

يُعد نمط الأمر مثالياً لجدولة العمليات، أو تنفيذ خاصية التراجع، أو تسجيل الإجراءات. كل أمر يكون مستقلاً بذاته، مما يسهل إضافة أوامر جديدة دون تعديل الكود الحالي.

جرّب بنفسك

<?php

require_once 'Commands.php';

// قراءة المدخلات
$commandType = trim(fgets(STDIN)); // "write" أو "clear"
$text = trim(fgets(STDIN));         // النص الخاص بأمر الكتابة

// TODO: إنشاء مثيل (instance) من TextEditor

// TODO: بناءً على نوع الأمر، قم بإنشاء كائن الأمر المناسب
// - إذا كان "write"، قم بإنشاء WriteCommand مع المحرر والنص
// - إذا كان "clear"، قم بإنشاء ClearCommand مع المحرر

// TODO: إنشاء آلية استدعاء (invoker) تقوم بتنفيذ execute() على الأمر
// وطباعة النتيجة

// TODO: طباعة المحتوى الحالي للمحرر بالتنسيق: "Content: [content]"
// (إذا كان فارغاً، فقط اطبع "Content: " دون أي شيء بعد المسافة)

?>
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس Object Oriented Programming