أنماط التصاميم. نمط الأمر
() translation by (you can also view the original English article)
بعد تغطية البعض من أنماط (نماذج) التصاميم الانشائية و الهيكلية . إنتقلنا للتحدث عن الأنماط السلوكية حيث بدأنا بالنمط السلوكي الأول في هذه المجموعة (النمط أو النموذج الأستراتيجي). لقد تعلمنا في الأنماط الإنشائية كيفية إنشاء الأغراض, ومن النماذج الهيكلية كيفية ترتيب وهيكلة الصفوف لتساعدنا في بناء تطبيق أفضل.
في هذه المقالة شو نقوم بالتعرف على نمط الأمر. الإسم بشكل عام يعرف نفسه, هذا النمط يساعد على تنفيد أوامر مختلفة. التعريف لهذا النمط بالويكبيديا هو كالتالي:
نمط الأمر هو نموذج تصميمي سلوكي بحيث أن إنشاء غرض منه يستخدم ليمثل ويغلف جميع المعطيات اللازمة لإستدعاء ميثود معينة لغرض آخر في وقت لاحق. والمقصود بالمعطيات هي كل شيء بدءاً بإسم الميثود و الغرض المالك لها وإنتهاءاً يقيم المتحولات لهذه الميثود التي سيتم تنفيذها.
بشكل عام يتم إشتراك عدة عناصر في نموذج الأمر. سوف نقوم بالتعرف على كل عنصر على حدا مع كتابة كود (ترميز) برمجي خاص بها , ومن ثم سوف نقوم بتجميع العناصر لتشكيل الكود الكامل لنمط الأمر. المثال الأكثر تطرقا عند المرور بنموذج الأمر هو مثال تشغيل و إطفاء الراديو on و off . دعونا نتعرف على العناصر المكونة لهذا النمط
المُستقْبٍل Receiver :
وهو الصف الأساسي, بمعنى آخر , هو الكود الأساسي أو مايعرف بال actual implementation وهو عبارة عن مجموعة أوامر يقوم الصف بتنفيذها. يمكن أيضا الحفاظ على الأوامر التي تم تنفيذها, ولكن هذه المهمة ليست جزء من نموذج الأمر لذلك سوف نتحدث عنها لاحقا في مقالة النموذج التذكاري.
1 |
// Receiver
|
2 |
class radioControl { |
3 |
public function turnOn() { |
4 |
// Turning On Radio
|
5 |
echo "Turning On Radio"; |
6 |
}
|
7 |
public function turnOff() { |
8 |
// Turning Off Radio
|
9 |
echo "Turning Off Radio"; |
10 |
}
|
11 |
}
|
الأمر Command :
هذا العنصر هو عبارة عن عدة صفوف (صف لكل أمر) وهو يحتوي على المعلومات اللازمة لتنفيذ الحدث (الأمر) الخاص به. كل صف من هذه الصفوف يقوم بإستدعاء الميثود المطلوبة منه من المستقبل receiver
. مايميز هذه الصفوف هي إنها جميعها تقوم بوراثة واجهة radioCommand بحيث تقوم بتغليف كل أمر من أوامر المستقبل ضمن ميثود موحدة في هذه الصفوف execute
.
1 |
// Command
|
2 |
interface radioCommand { |
3 |
public function execute(); |
4 |
}
|
5 |
|
6 |
class turnOnRadio implements radioCommand { |
7 |
private $radioControl; |
8 |
public function __construct(radioControl $radioControl) { |
9 |
$this->radioControl = $radioControl; |
10 |
}
|
11 |
public function execute() { |
12 |
$this->radioControl->turnOn (); |
13 |
}
|
14 |
}
|
15 |
|
16 |
class turnOffRadio implements radioCommand { |
17 |
private $radioControl; |
18 |
public function __construct(radioControl $radioControl) { |
19 |
$this->radioControl = $radioControl; |
20 |
}
|
21 |
public function execute() { |
22 |
$this->radioControl->turnOff (); |
23 |
}
|
24 |
}
|
الزبون Client :
هذا العنصر يتصرف تماما كالزبون (يتخذ قرار ما سيقوم بفعله). يختار عنصر الزبون الأمر الذي يريد أن ينفذه ويسنده لمتحول. هذا العنصر لايكترث بمن سيقوم تنفيذ هذا الأمر, ولا بكيفيه تنفيذه. في هذا المثال قمت بإتخاذ الأمر وإسناده كقيمة ثابتة hard-coded . ولكن القيمة ممكن أن تكون بإي شكل , ممكن أن يتم أخذها من فورم تعبئة POST أو من أي URL.
1 |
// Client
|
2 |
$in = 'turnOffRadio'; |
المستدعي Invoker :
هذا العنصر يقوم بإستهلال (تهيئة) العملية بشكل كامل. حيث يقوم بأخذ المتحولات من الزبون client و إستدعاء الميثود لتنفذ الأمر المطلوب.
1 |
// Invoker
|
2 |
if (class_exists ( $in )) { |
3 |
$command = new $in ( new radioControl () ); |
4 |
} else { |
5 |
throw new Exception ( '..Command Not Found..' ); |
6 |
}
|
7 |
|
8 |
$command->execute (); |
عند تنفيد الكود السابق سوف نلاحظ بأن الخرج هو "Turning Off Radio" . طبعا لأن الزبون هو الذي حدد هذا الأمر في الفقرة السابقة. الآن نستطيع تنفيذ أوامر اُخرى بنفس الطريقة. إذا كان الزبون حدد الحدث ك turnOnRadio
. سيكون الخرج "Turning On Radio". الموضوع بهذه البساطة!
جمع النتيجة ككل:
دعونا نجمع جميع العناصر في مكان واحد لنرى كيف يتم تنفيذ نموذج الأمر.
1 |
// Receiver
|
2 |
class radioControl { |
3 |
public function turnOn() { |
4 |
// Turning On Radio
|
5 |
echo "Turning On Radio"; |
6 |
}
|
7 |
public function turnOff() { |
8 |
// Turning Off Radio
|
9 |
echo "Turning Off Radio"; |
10 |
}
|
11 |
}
|
12 |
|
13 |
// Command
|
14 |
interface radioCommand { |
15 |
public function execute(); |
16 |
}
|
17 |
|
18 |
class turnOnRadio implements radioCommand { |
19 |
private $radioControl; |
20 |
public function __construct(radioControl $radioControl) { |
21 |
$this->radioControl = $radioControl; |
22 |
}
|
23 |
public function execute() { |
24 |
$this->radioControl->turnOn (); |
25 |
}
|
26 |
}
|
27 |
|
28 |
class turnOffRadio implements radioCommand { |
29 |
private $radioControl; |
30 |
public function __construct(radioControl $radioControl) { |
31 |
$this->radioControl = $radioControl; |
32 |
}
|
33 |
public function execute() { |
34 |
$this->radioControl->turnOff (); |
35 |
}
|
36 |
}
|
37 |
|
38 |
|
39 |
// Client
|
40 |
$in = 'turnOffRadio'; |
41 |
|
42 |
// Invoker
|
43 |
if (class_exists ( $in )) { |
44 |
$command = new $in ( new radioControl () ); |
45 |
} else { |
46 |
throw new Exception ( '..Command Not Found..' ); |
47 |
}
|
48 |
|
49 |
$command->execute (); |
إضافة أوامر جديدة :
قبل قليل كان لدينا فقط تردد واحد للراديو الخاص بنا, لذلك اكتفينا بإطفائه وتشغيله. لكن دعونا نتخيل مع مرور الوقت أنه قد اصبح لدينا عدة ترددات , نحن الآن بحاجة إلى ميثودات إضافية tuneUp
و tuneDown
لنرى كيف سوف نقوم بإضافة هذه الأوامر.
عندما نقوم بإستخدام نماذج التصميم, تصبح الإضافة أو التعديل لوظائف التطبيق الخاص بنا أسهل بكثير ودون الحاجة إلى إجراء عدة تغييرات أو التغيير في كود الزبون. وهذا ينطبق تماماً على مثالنا هذا. سوف نقوم بإضافة الأمرين السابقين , ولكننا لن نقوم بأي تعديل على الكود الخاص بعنصري الزبون والمستدعي .
إضافة أمر جديد يتطلب التعديل في مكانين.الأمر الأول هو أن نقوم بإضافة الميثود الجديدة الخاصة بالأمر المطلوب في التطبيق والتي تحتوي المنطق الخاص بالأمر , بمعنى آخر يجب إضافة التطبيق للميثود في عنصر المستقبل. أما الامر الثاني فهو إنشاء صف جديد لإستدعاء الميثود السابقة (وهو صف في عنصر الأمر كما سميناه سابقا) ويجب لهذا الصف ان يقوم بوراثة الواجهة radioCommand
وإستدعاء الميثود الموجودة في عنصر المستقبل.
الأوامر الجديدة
1 |
class tuneUpRadio implements radioCommand { |
2 |
private $radioControl; |
3 |
public function __construct(radioControl $radioControl) { |
4 |
$this->radioControl = $radioControl; |
5 |
}
|
6 |
public function execute() { |
7 |
$this->radioControl->tuneUp (); |
8 |
}
|
9 |
}
|
10 |
|
11 |
class tuneDownRadio implements radioCommand { |
12 |
private $radioControl; |
13 |
public function __construct(radioControl $radioControl) { |
14 |
$this->radioControl = $radioControl; |
15 |
}
|
16 |
public function execute() { |
17 |
$this->radioControl->tuneDown (); |
18 |
}
|
19 |
}
|
التعديل على ترميز (كود) المُستقْبِل Receiver :
سوف نقوم بإضافة ميثودين جديدتين على صف المُستقْبٍل
1 |
// Receiver
|
2 |
class radioControl { |
3 |
public function turnOn() { |
4 |
// Turning On Radio
|
5 |
echo "Turning On Radio"; |
6 |
}
|
7 |
public function turnOff() { |
8 |
// Turning Off Radio
|
9 |
echo "Turning Off Radio"; |
10 |
}
|
11 |
|
12 |
public function tuneUp() { |
13 |
// Tuning Up Radio
|
14 |
echo "Tuning Up Radio"; |
15 |
}
|
16 |
|
17 |
public function tuneDown() { |
18 |
// Tuning Down Radio
|
19 |
echo "Tuning Down Radio"; |
20 |
}
|
21 |
}
|
النتيجة :
يجب علينا إستخدام نموذج الأمر عندما يكون لدينا أوامر متعددة نريد تنفيذها, ولا يهم إن كانت هذه الأوامر مترابطة أم لا. أخيراً أود أن اقول أنني حاولت ما بوسعي إلى تفصيل هذا النمط التصميمي أتمنى أن تعم الفائدة. يمكنكم أن تقومو بوضع تعليق لمعرفة أرائكم.