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

نمط الأمر

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

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

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

يتكوّن النمط من أربعة مكوّنات رئيسية: واجهة Command تعلن عن method ‏execute، وفئات ConcreteCommand التي تنفّذ actions محددة، وReceiver الذي ينفّذ العمل actual، وInvoker الذي يطلق الأوامر:

interface Command {
    void execute();
}
class Light {  // المستلم
    public void turnOn() {
        System.out.println("Light is ON");
    }
    public void turnOff() {
        System.out.println("Light is OFF");
    }
}
class LightOnCommand implements Command {
    private Light light;
    
    public LightOnCommand(Light light) {
        this.light = light;
    }
    
    public void execute() {
        light.turnOn();
    }
}
class RemoteControl {  // المُستدعي
    private Command command;
    
    public void setCommand(Command command) {
        this.command = command;
    }
    
    public void pressButton() {
        command.execute();
    }
}

لا يعرف invoker أي إجراء سيتم تنفيذه. فهو يستدعي فقط execute():

Light light = new Light();
Command lightOn = new LightOnCommand(light);

RemoteControl remote = new RemoteControl();
remote.setCommand(lightOn);
remote.pressButton();  // الناتج: Light is ON

يتفوق نمط Command عندما تحتاج إلى وضع العمليات في قائمة انتظار، أو Implement وظيفة التراجع/الإعادة، أو جدولة المهام لتنفيذها لاحقًا. ي encapsulate كل أمر كل ما يلزم لتنفيذ إجراء ما، مما يجعل نظامك أكثر مرونة وقابلية للتوسعة.

challenge icon

التحدي

سهل

لنبنِ نظامًا ذكيًا لأتمتة المنزل باستخدام نمط Command! ستنشئ نظامًا يمكن فيه التحكم في أجهزة منزلية مختلفة من خلال أوامر، مما يتيح وضع actions في قائمة انتظار وتنفيذها وحتى تخزينها لاستخدامها لاحقًا، تمامًا مثل جهاز تحكم عن بُعد عالمي لا يحتاج إلى معرفة كيفية عمل كل جهاز من الداخل.

ستنظّم التعليمات البرمجية لديك عبر أربعة ملفات:

  • Command.java: عرّف Command interface التي ستنفذها جميع أوامر الأجهزة. يجب أن تعلن عن method واحدة هي execute() التي تنفذ action الخاصة بالأمر.
  • Devices.java: أنشئ فئتي receiver تمثلان أجهزة المنزل الذكي:

    Television - تحتوي على methods هي powerOn() التي تطبع TV is now ON، وpowerOff() التي تطبع TV is now OFF، وsetChannel(int channel) التي تطبع TV channel set to [channel].

    Thermostat - تحتوي على methods هي setTemperature(int temp) التي تطبع Thermostat set to [temp] degrees وturnOff() التي تطبع Thermostat turned OFF.

  • Commands.java: أنشئ فئات أوامر Concrete تعمل على encapsulate عمليات الأجهزة:

    TVOnCommand - تستقبل Television في constructor الخاص بها وتستدعي powerOn() عند تنفيذها.

    TVOffCommand - تستقبل Television في constructor الخاص بها وتستدعي powerOff() عند تنفيذها.

    TVChannelCommand - تستقبل Television وint channel في constructor الخاص بها، وتستدعي setChannel() باستخدام channel تلك عند تنفيذها.

    ThermostatSetCommand - تستقبل Thermostat وint temperature في constructor الخاص بها، وتستدعي setTemperature() باستخدام تلك القيمة عند تنفيذها.

  • Main.java: أنشئ فئة RemoteControl (وهي invoker) تحتوي على method هي setCommand(Command cmd) لتخزين أمر، وpressButton() لتنفيذ الأمر المخزّن.

    في main method، ستتلقى inputين: رقم channel التلفاز (عدد صحيح) ودرجة حرارة thermostat (عدد صحيح).

    أنشئ Television وThermostat. أنشئ RemoteControl وDemonstrate نمط Command من خلال:

    1. تعيين TVOnCommand والضغط على الزر
    2. تعيين TVChannelCommand باستخدام channel المدخل والضغط على الزر
    3. تعيين ThermostatSetCommand باستخدام temperature المدخلة والضغط على الزر
    4. تعيين TVOffCommand والضغط على الزر

ستتلقى inputين بالترتيب: channel التلفاز (عدد صحيح) ودرجة حرارة thermostat (عدد صحيح).

على سبيل المثال، عند إدخال 5 و72، سيكون الناتج كما يلي:

TV is now ON
TV channel set to 5
Thermostat set to 72 degrees
TV is now OFF

لاحظ أن RemoteControl لا يتفاعل مباشرةً مع الأجهزة أبدًا. فهو يعرف فقط Command interface. كل أمر يعمل على encapsulate كل ما يلزم لتنفيذ action الخاصة به، مما يسهّل إضافة أجهزة وأوامر جديدة دون تغيير invoker!

جرّب بنفسك

import java.util.Scanner;

// TODO: إنشاء فئة RemoteControl (المستدعي) مع:
// - دالة setCommand(Command cmd) لتخزين أمر
// - دالة pressButton() لتنفيذ الأمر المخزن

class RemoteControl {
    // TODO: تطبيق RemoteControl
}

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // قراءة المدخلات
        int channel = scanner.nextInt();
        int temperature = scanner.nextInt();
        
        // TODO: إنشاء Television و Thermostat
        
        // TODO: إنشاء RemoteControl
        
        // TODO: توضيح نمط الأمر (Command Pattern):
        // 1. تعيين TVOnCommand والضغط على الزر
        // 2. تعيين TVChannelCommand مع قناة الإدخال والضغط على الزر
        // 3. Set a ThermostatSetCommand with the input temperature and press the button
        // 4. تعيين TVOffCommand والضغط على الزر
        
        scanner.close();
    }
}
quiz iconاختبر نفسك

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

جميع دروس البرمجة كائنية التوجه

تدرّب بنفسك: مترجم Java عبر الإنترنت