نمط الأمر
جزء من قسم البرمجة كائنية التوجه في رحلة 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 كل أمر كل ما يلزم لتنفيذ إجراء ما، مما يجعل نظامك أكثر مرونة وقابلية للتوسعة.
التحدي
سهللنبنِ نظامًا ذكيًا لأتمتة المنزل باستخدام نمط Command! ستنشئ نظامًا يمكن فيه التحكم في أجهزة منزلية مختلفة من خلال أوامر، مما يتيح وضع actions في قائمة انتظار وتنفيذها وحتى تخزينها لاستخدامها لاحقًا، تمامًا مثل جهاز تحكم عن بُعد عالمي لا يحتاج إلى معرفة كيفية عمل كل جهاز من الداخل.
ستنظّم التعليمات البرمجية لديك عبر أربعة ملفات:
Command.java: عرّفCommandinterface التي ستنفذها جميع أوامر الأجهزة. يجب أن تعلن عن 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 من خلال:- تعيين
TVOnCommandوالضغط على الزر - تعيين
TVChannelCommandباستخدام channel المدخل والضغط على الزر - تعيين
ThermostatSetCommandباستخدام temperature المدخلة والضغط على الزر - تعيين
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();
}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
1أساسيات الـ OOP
الملفات الخارجيةمقدمة في الـ OOPالفئات (Classes) مقابل الكائنات (Objects)الكلمة المفتاحية thisالدوال (Methods)الحقول (Attributes)دالة البناء (Constructor)تعدد أشكال دالة البناء (Constructor Overloading)مراجعة - آلة حاسبة بسيطة4الوراثة
الوراثة الأساسية (extends)الكلمة المفتاحية superإعادة تعريف الدوال (@Override)تسلسل المنشئات (Constructor Chaining)كلاس Objectالوراثة الأحادية ومتعددة المستوياتلماذا لا توجد وراثة متعددة للكلاساتمراجعة - هيكلية الموظفين7الدوال الخاصة وفئة Object
دالة toString()equals() و hashCode()دالة clone()compareTo() و Comparableواجهة Comparatorمراجعة - الترتيب المخصص2محددات الوصول والتغليف
نظرة عامة على مستويات الوصولدوال Getter و Setterإخفاء البياناتالكلمة المفتاحية finalمراجعة - مدير الحساب البنكي5تعدد الأشكال (Polymorphism)
أساسيات الـ Method Overloadingالـ Method Overriding (في وقت التشغيل)الـ Upcasting والـ Downcastingمعامل الـ instanceofالفئات والدوال المجردة (Abstract)مراجعة - حاسبة الأشكال8مفاهيم OOP المتقدمة
الـ Composition مقابل الـ Inheritanceالـ Aggregation مقابل الـ Compositionالفئات الداخلية والمتداخلة والمجهولة (Inner, Nested & Anonymous)الـ Enums ودوالهاالـ Records (Java 16+)الـ Sealed Classes (Java 17+)11أنماط التصميم - الجزء الأول
مقدمة في أنماط التصميمنمط Singletonنمط Factoryنمط Builderنمط Observerنمط Strategy3خصائص الفئة والعضو الساكن
المتغيرات الخاصة بالكائن مقابل المتغيرات الساكنةالدوال الساكنةالكتل البرمجية الساكنةالثوابت (static final)ملخص - العداد والأدوات المساعدة6الواجهات (Interfaces) والأصناف المجردة (Abstract Classes)
مقدمة في الواجهات (Interfaces)تنفيذ الواجهات (Interfaces)تنفيذ واجهات متعددةالـ Default و Static في الواجهاتالأصناف المجردة مقابل الواجهاتالواجهات الوظيفية (Functional Interfaces)مراجعة - نظام الدفع9الأنواع العامة (Generics)
مقدمة في الأنواع العامةالأصناف العامة (Generic Classes)الدوال العامة (Generic Methods)معاملات الأنواع المقيدةالرموز البديلة (?, extends, super)مراجعة - الحاويات العامة12أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبنمط المكررتدرّب بنفسك: مترجم Java عبر الإنترنت