نمط الأمر
جزء من قسم البرمجة كائنية التوجه في رحلة PHP على Coddy. الدرس 75 من 91.
إن Command Pattern هو نمط تصميم سلوكي يحوّل طلبًا إلى كائن مستقل يحتوي على جميع المعلومات المتعلقة بالطلب. يتيح لك هذا التحويل تمرير الطلبات كوسائط methods، وتأخير تنفيذ الطلب أو وضعه في قائمة انتظار، ودعم العمليات القابلة للتراجع.
تخيّل مطعمًا.
عندما تطلب الطعام، يكتب النادل طلبك على إيصال (command)، ثم يأخذه إلى المطبخ، ويقوم الطاهي بتنفيذه. لا يحتاج النادل إلى معرفة كيفية الطهي؛ فهو يكتفي بتسليم command.
يفصل هذا بين مُقدِّم الطلب والمنفِّذ.
إليك تنفيذًا أساسيًا:
<?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 عندما تحتاج إلى وضع العمليات في قائمة انتظار، أو Implement وظيفة التراجع، أو تسجيل الإجراءات. كل command مستقل بذاته، مما يجعل من السهل إضافة commands جديدة دون تعديل الشفرة الموجودة.
التحدي
سهللنَبْنِ نظام أوامر لمحرر نصوص باستخدام نمط Command. غالبًا ما تحتاج محررات النصوص إلى دعم عمليات مثل كتابة النص وحذف النص والتراجع عن الإجراءات: تصبح كل عملية كائن أمر مستقلًا يمكن تنفيذه من خلال واجهة موحّدة.
ستُنظّم الشيفرة عبر أربعة ملفات:
Command.php: Define واجهةCommandذات method واحدة هيexecute(): string. وهذا يحدد contract الذي يجب أن تتبعه جميع أوامر المحرر.TextEditor.php، أنشئ classTextEditorتمثل المستقبل، أي الكائن الذي ينفذ العمل فعليًا. يجب أن يقوم المحرر بما يلي:- تخزين محتوى النص current (ابدأ بسلسلة نصية empty)
- امتلاك method
write(string $text): stringتُلحِق النص بالمحتوى وتعيد"Written: [text]" - امتلاك method
clear(): stringتُفرغ المحتوى وتعيد"Content cleared" - امتلاك method
getContent(): stringالتي تعيد المحتوى current
Commands.php: أدرج ملفي Command وTextEditor. أنشئ classين للأوامر ينفذان واجهةCommand:WriteCommand: تأخذTextEditorوسلسلة نصية في constructor الخاص بها. تستدعي methodexecute()الخاصة بها methodwrite()للمحرر وتعيد النتيجة.ClearCommand: تأخذTextEditorفي constructor الخاص بها. تستدعي methodexecute()الخاصة بها methodclear()للمحرر وتعيد النتيجة.
main.php: أدرج ملف Commands. ستتلقى مدخلين: نوع command ("write"أو"clear") وسلسلة نصية (تُستخدم فقط مع أوامر الكتابة).أنشئ instance من
TextEditor. وبناءً على نوع command، أنشئ كائن الأمر appropriate. ثم أنشئ function invoker أو آلية بسيطة تستدعيexecute()على الأمر وتطبع النتيجة.في سطر جديد، اطبع المحتوى current للمحرر بهذا format:
Content: [content](إذا كان فارغًا، اطبع فقطContent:دون أي شيء بعد المسافة).
يوضح هذا التحدي كيف يقوم نمط Command بتغليف كل عملية في شكل كائن. لا يحتاج invoker إلى معرفة ما إذا كان ينفذ command للكتابة أو للمسح. فهو يستدعي ببساطة execute() ويترك للأمر التعامل مع التفاصيل.
جرّب بنفسك
<?php
require_once 'Commands.php';
// اقرأ الإدخال
$commandType = trim(fgets(STDIN)); // "write" أو "clear"
$text = trim(fgets(STDIN)); // النص لأمر write
// TODO: أنشئ مثيلاً من TextEditor
// TODO: بناءً على نوع الأمر، أنشئ كائن الأمر المناسب
// - إذا كان "write"، أنشئ WriteCommand مع المحرر والنص
// - إذا كان "clear"، أنشئ ClearCommand مع المحرر
// TODO: أنشئ آلية invoker تستدعي execute() على الأمر
// وتطبع النتيجة
// TODO: اطبع المحتوى الحالي للمحرر بالتنسيق: "Content: [content]"
// (إذا كان فارغًا، اطبع فقط "Content: " بدون أي شيء بعد المسافة)
?>يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
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)تدرّب بنفسك: مترجم PHP عبر الإنترنت