نمط الأمر
جزء من قسم 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) عندما تحتاج إلى وضع العمليات في طابور، أو تنفيذ خاصية التراجع، أو تسجيل الإجراءات. كل أمر يكون قائماً بذاته، مما يسهل إضافة أوامر جديدة دون تعديل الكود الحالي.
التحدي
سهللنقم ببناء نظام أوامر لمحرر نصوص باستخدام نمط الأمر (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: " دون أي شيء بعد المسافة)
?>يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات OOP
الملفات الخارجيةمقدمة في OOPClasses مقابل Objectsالكلمة المفتاحية $thisالـ Methodsالـ Propertiesالمنشئ (__construct)الهادم (__destruct)مراجعة - آلة حاسبة بسيطة4الوراثة
الوراثة الأساسيةالكلمة المفتاحية parent::إعادة تعريف الدوال (Method Overriding)الكلمة المفتاحية finalالأصناف المجردة (Abstract Classes)مراجعة - هيكلية الموظفين7التغليف (Encapsulation)
العام، المحمي، الخاص (Public, Protected, Private)نظرة متعمقة على معدلات الوصولدوال Getters و Settersإخفاء المعلوماتميزة Constructor Promotion (8.0)مراجعة - نظام سجلات الطلاب10مفاهيم البرمجة كائنية التوجه المتقدمة
التركيب مقابل الوراثةحقن التبعيةالأصناف المجهولةالتعدادات (PHP 8.1)الألياف (PHP 8.1)تعمق في استنساخ الكائناتالمولدات والمكررات13أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبنمط المستودع2مساحات الأسماء والتحميل التلقائي
مقدمة إلى مساحات الأسماءالكلمة المفتاحية useمعيار التحميل التلقائي PSR-4أداة التحميل التلقائي Composerملخص - مشروع منظم5الواجهات والعقود
مقدمة إلى الواجهاتتنفيذ الواجهاتتنفيذ واجهات متعددةالواجهة مقابل الـ Abstract Classالـ Type Hinting مع الواجهاتملخص - حاسبة الأشكال8الميثودات السحرية (Magic Methods)
مقدمة في الميثودات السحرية__toString و __debugInfo__get, __set, __isset, __unset__call و __callStatic__clone واستنساخ الكائنات__serialize و __unserializeمراجعة - Custom Collection3خصائص الكلاس
خصائص الـ Instance مقابل الخصائص الساكنة (Static)الثوابت في الكلاساتالدوال والخصائص الساكنة (Static)الخصائص الخاصة (Private) والمحمية (Protected)خصائص للقراءة فقط (Readonly) في PHP 8.1مراجعة - مدير الحساب البنكي6تعدد الأشكال (Polymorphism)
مراجعة إعادة تعريف الدوال (Method Overriding)تعدد الأشكال عبر الـ Interfacesتلميح النوع (Type Hinting) وأنواع الاتحاد (Union Types)الربط الاستاتيكي المتأخر (Late Static Binding)ملخص - معالج الدفع (Payment Processor)