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

نمط الأمر

جزء من قسم Object Oriented Programming في رحلة Dart على Coddy — الدرس 94 من 110.

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

يتضمن النمط ثلاثة مكونات رئيسية: واجهة Command تحتوي على طريقة execute، و Concrete Commands التي تنفذ إجراءات محددة، و Receiver الذي يقوم بالعمل الفعلي. يقوم Invoker اختياري بتشغيل الأوامر دون معرفة ما تفعله:

abstract class Command {
  void execute();
}

class Light {
  void turnOn() => print('Light is ON');
  void turnOff() => print('Light is OFF');
}

class TurnOnCommand implements Command {
  final Light light;
  TurnOnCommand(this.light);

  @override
  void execute() => light.turnOn();
}

class TurnOffCommand implements Command {
  final Light light;
  TurnOffCommand(this.light);

  @override
  void execute() => light.turnOff();
}

class RemoteControl {
  Command? _command;

  void setCommand(Command command) {
    _command = command;
  }

  void pressButton() {
    _command?.execute();
  }
}

void main() {
  var light = Light();
  var remote = RemoteControl();

  remote.setCommand(TurnOnCommand(light));
  remote.pressButton();  // Light is ON

  remote.setCommand(TurnOffCommand(light));
  remote.pressButton();  // Light is OFF
}

لا يعرف الـ RemoteControl أي شيء عن الأضواء - فهو ينفذ فقط أي أمر يُعطى له. هذا الفصل يجعل من السهل إضافة أوامر جديدة دون تعديل المستدعي (invoker). يُعد نمط الأمر (Command pattern) مثاليًا لتنفيذ أنظمة التراجع/الإعادة، أو طوابير المهام، أو تسجيل الماكرو حيث تحتاج إلى التعامل مع الإجراءات ككائنات من الدرجة الأولى.

challenge icon

التحدي

سهل

لنقم ببناء نظام أوامر لمحرر نصوص باستخدام نمط الأمر (Command pattern)! ستقوم بإنشاء نظام يغلف عمليات النص ككائنات أوامر، مما يسمح للمحرر بتنفيذ إجراءات مختلفة دون معرفة تفاصيل كل عملية. هذه حالة استخدام كلاسيكية لنمط الأمر - فكر فيها كنظام تراجع/إعادة (undo/redo) حيث يتم تخزين كل إجراء ككائن.

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

  • commands.dart: أنشئ نظام الأوامر الخاص بك هنا. ابدأ بفئة مجردة (abstract class) تسمى Command تعلن عن طريقة execute(). ثم أنشئ فئة TextDocument (المستقبل - receiver) التي تحمل حقل String content (فارغ في البداية) ولديها ثلاث طرق:
    • append(String text) - يضيف النص إلى نهاية المحتوى
    • clear() - يضبط المحتوى إلى سلسلة فارغة
    • show() - يطبع Document: [content]
    الآن قم بتنفيذ ثلاثة أوامر ملموسة (concrete commands) تعمل مع TextDocument:
    • AppendCommand - يأخذ مستنداً ونصاً في منشئه (constructor)، ويقوم بإلحاق النص عند التنفيذ
    • ClearCommand - يأخذ مستنداً في منشئه، ويقوم بمسحه عند التنفيذ
    • ShowCommand - يأخذ مستنداً في منشئه، ويعرض محتواه عند التنفيذ
    أخيراً، أنشئ فئة TextEditor (المستدعي - invoker) التي تخزن قائمة من الأوامر. يجب أن تحتوي على طريقة addCommand(Command command) لإضافة الأوامر إلى قائمة الانتظار، وطريقة runAll() التي تنفذ جميع الأوامر الموجودة في القائمة بالترتيب.
  • main.dart: استورد ملف الأوامر الخاص بك واستعرض نمط الأمر. أنشئ TextDocument و TextEditor. قم بجدولة الأوامر التالية بالترتيب: إلحاق Hello، إلحاق World، عرض المستند، إلحاق !، العرض مرة أخرى، مسح المستند، والعرض لمرة أخيرة. ثم قم بتشغيل جميع الأوامر المجدولة.

لاحظ كيف أن TextEditor لا يعرف أي شيء عن الإلحاق أو المسح أو العرض - فهو ينفذ فقط أي أوامر تُعطى له. هذا الفصل (decoupling) هو جوهر نمط الأمر!

المخرجات المتوقعة:

Document: Hello World
Document: Hello World!
Document: 

جرّب بنفسك

import 'commands.dart';

void main() {
  // TODO: إنشاء مثيل من TextDocument

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

  // TODO: قم بوضع الأوامر التالية في قائمة الانتظار بالترتيب:
  // 1. إلحاق "Hello"
  // 2. إلحاق " World"
  // 3. عرض المستند
  // 4. إلحاق "!"
  // 5. عرض المستند مرة أخرى
  // 6. مسح المستند
  // 7. عرض المستند للمرة الأخيرة

  // TODO: تشغيل جميع الأوامر الموجودة في قائمة الانتظار
}
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming