الاستراتيجية.
الاستراتيجية هو نمط تصميم سلوكي يتيح لك تعريف عائلة من الخوارزميات، ووضع كل منها في فئة منفصلة، وجعل كائناتها قابلة للتبادل.
##Intent
الاستراتيجية هو نمط تصميم سلوكي يتيح لك تعريف عائلة من الخوارزميات، ووضع كل منها في فئة منفصلة، وجعل كائناتها قابلة للتبادل.

##Problem
في يوم من الأيام، قررت إنشاء تطبيق ملاحة للمسافرين العاديين. كان التطبيق يتمحور حول خريطة جميلة ساعدت المستخدمين على التوجه بسرعة في أي مدينة.
كانت أكثر الميزات المطلوبة في التطبيق هي التخطيط التلقائي للمسارات. يجب أن يتمكن المستخدم من إدخال عنوان ورؤية أسرع مسار إلى تلك الوجهة معروضاً على الخريطة.
كان الإصدار الأول من التطبيق قادراً فقط على بناء المسارات عبر الطرق. كان الناس الذين يسافرون بالسيارة يشعرون بالبهجة. لكن على ما يبدو، لا يحب الجميع القيادة في إجازتهم. لذا مع التحديث التالي، أضفت خياراً لبناء مسارات المشاة. وبعدها مباشرةً، أضفت خياراً آخر يتيح للناس استخدام وسائل النقل العام في مساراتهم.
غير أن ذلك كان مجرد البداية. خططت لاحقاً لإضافة بناء مسارات لراكبي الدراجات. وحتى في وقت لاحق، خياراً آخر لبناء مسارات عبر جميع المعالم السياحية في المدينة.

أصبح كود تطبيق الملاحة مفرطاً في الحجم.
وعلى الرغم من نجاح التطبيق من الناحية التجارية، فإن الجانب التقني سبّب لك صداعاً كبيراً. في كل مرة كنت تضيف فيها خوارزمية توجيه جديدة، كانت الفئة الرئيسية لتطبيق الملاحة تتضاعف في حجمها. في مرحلة ما، أصبح هذا الوحش صعب الصيانة للغاية.
أي تغيير على إحدى الخوارزميات، سواء كان إصلاحاً بسيطاً لخطأ أو تعديلاً طفيفاً لدرجة الشارع، أثّر على الفئة بأكملها، مما زاد من احتمالية إنشاء خطأ في كود يعمل بالفعل.
علاوة على ذلك، أصبح العمل الجماعي غير فعّال. يشكو زملاؤك، الذين تم تعيينهم بعد الإصدار الناجح مباشرةً، من أنهم يقضون وقتاً طويلاً جداً في حل تعارضات الدمج. يتطلب تطبيق ميزة جديدة تغيير نفس الفئة الضخمة، مما يتعارض مع الكود الذي ينتجه الآخرون.
##Solution
يقترح نمط الاستراتيجية أن تأخذ فئة تفعل شيئاً محدداً بطرق كثيرة مختلفة وتستخرج جميع هذه الخوارزميات إلى فئات منفصلة تُسمى الاستراتيجيات.
يجب أن تحتوي الفئة الأصلية، المسماة السياق، على حقل لتخزين مرجع إلى إحدى الاستراتيجيات. يفوّض السياق العمل إلى كائن استراتيجية مرتبط بدلاً من تنفيذه بنفسه.
السياق ليس مسؤولاً عن اختيار الخوارزمية المناسبة للمهمة. بدلاً من ذلك، يُمرر العميل الاستراتيجية المطلوبة إلى السياق. في الواقع، لا يعرف السياق الكثير عن الاستراتيجيات. يعمل مع جميع الاستراتيجيات من خلال نفس الواجهة العامة، التي تعرض أسلوباً واحداً فقط لتشغيل الخوارزمية المغلّفة داخل الاستراتيجية المختارة.
بهذه الطريقة يصبح السياق مستقلاً عن الاستراتيجيات المحددة، مما يتيح لك إضافة خوارزميات جديدة أو تعديل الموجودة دون تغيير كود السياق أو الاستراتيجيات الأخرى.

استراتيجيات تخطيط المسارات.
في تطبيق الملاحة الخاص بنا، يمكن استخراج كل خوارزمية توجيه إلى فئتها الخاصة مع أسلوب buildRoute واحد. يقبل الأسلوب نقطة أصل ووجهة ويعيد مجموعة من نقاط تفتيش المسار.
على الرغم من أنه بنفس الوسيطات، قد تبني كل فئة توجيه مساراً مختلفاً، فإن الفئة الرئيسية لتطبيق الملاحة لا تهتم حقاً بالخوارزمية المختارة إذ إن مهمتها الأساسية هي عرض مجموعة من نقاط التفتيش على الخريطة. تمتلك الفئة أسلوباً للتبديل بين استراتيجيات التوجيه النشطة، لذا يمكن لعملائها، كأزرار واجهة المستخدم، استبدال سلوك التوجيه المحدد حالياً بآخر.
##Structure


-
يحتفظ السياق بمرجع إلى إحدى الاستراتيجيات المحددة ويتواصل مع هذا الكائن فقط عبر واجهة الاستراتيجية.
-
واجهة الاستراتيجية مشتركة بين جميع الاستراتيجيات المحددة. تُعلن عن أسلوب يستخدمه السياق لتنفيذ استراتيجية.
-
الاستراتيجيات المحددة تُنفّذ متغيرات مختلفة من الخوارزمية التي يستخدمها السياق.
-
يستدعي السياق أسلوب التنفيذ على كائن الاستراتيجية المرتبط في كل مرة يحتاج فيها إلى تشغيل الخوارزمية. لا يعرف السياق نوع الاستراتيجية التي يعمل معها أو كيفية تنفيذ الخوارزمية.
-
يُنشئ العميل كائن استراتيجية محدداً ويمرره إلى السياق. يُعرض السياق دالة ضبط تتيح للعملاء استبدال الاستراتيجية المرتبطة بالسياق أثناء وقت التشغيل.
##Pseudocode
في هذا المثال، يستخدم السياق استراتيجيات متعددة لتنفيذ عمليات حسابية متنوعة.
// تُعلن واجهة الاستراتيجية عن العمليات المشتركة لجميع
// الإصدارات المدعومة من خوارزمية ما. يستخدم السياق هذه
// الواجهة لاستدعاء الخوارزمية المُعرَّفة بواسطة
// الاستراتيجيات المحددة.
interface Strategy is
method execute(a, b)
// تُنفّذ الاستراتيجيات المحددة الخوارزمية مع الالتزام
// بواجهة الاستراتيجية الأساسية. تجعلها الواجهة قابلة
// للتبادل في السياق.
class ConcreteStrategyAdd implements Strategy is
method execute(a, b) is
return a + b
class ConcreteStrategySubtract implements Strategy is
method execute(a, b) is
return a - b
class ConcreteStrategyMultiply implements Strategy is
method execute(a, b) is
return a * b
// يُحدد السياق الواجهة التي تهم العملاء.
class Context is
// يحتفظ السياق بمرجع إلى أحد كائنات الاستراتيجية.
// لا يعرف السياق الفئة المحددة للاستراتيجية. يجب أن يعمل
// مع جميع الاستراتيجيات عبر واجهة الاستراتيجية.
private strategy: Strategy
// عادةً يقبل السياق استراتيجية عبر المُنشئ، ويوفر أيضاً
// دالة ضبط لتمكين التبديل بين الاستراتيجيات أثناء وقت التشغيل.
method setStrategy(Strategy strategy) is
this.strategy = strategy
// يفوّض السياق بعض العمل إلى كائن الاستراتيجية
// بدلاً من تنفيذ نسخ متعددة من الخوارزمية بنفسه.
method executeStrategy(int a, int b) is
return strategy.execute(a, b)
// يختار كود العميل استراتيجية محددة ويمررها إلى السياق.
// يجب أن يكون العميل على دراية بالفروق بين الاستراتيجيات
// لاتخاذ الاختيار الصحيح.
class ExampleApplication is
method main() is
إنشاء كائن السياق.
قراءة الرقم الأول.
قراءة الرقم الأخير.
قراءة الإجراء المطلوب من إدخال المستخدم.
if (action == addition) then
context.setStrategy(new ConcreteStrategyAdd())
if (action == subtraction) then
context.setStrategy(new ConcreteStrategySubtract())
if (action == multiplication) then
context.setStrategy(new ConcreteStrategyMultiply())
result = context.executeStrategy(الرقم الأول, الرقم الثاني)
طباعة النتيجة.
##Applicability
استخدم نمط الاستراتيجية عندما تريد استخدام متغيرات مختلفة من خوارزمية داخل كائن والقدرة على التبديل من خوارزمية إلى أخرى أثناء وقت التشغيل.
يتيح لك نمط الاستراتيجية تغيير سلوك الكائن بشكل غير مباشر أثناء وقت التشغيل عن طريق ربطه بكائنات فرعية مختلفة يمكنها أداء مهام فرعية محددة بطرق مختلفة.
استخدم الاستراتيجية عندما يكون لديك الكثير من الفئات المتشابهة التي تختلف فقط في طريقة تنفيذها لبعض السلوك.
يتيح لك نمط الاستراتيجية استخراج السلوك المتغير إلى تسلسل هرمي منفصل للفئات ودمج الفئات الأصلية في فئة واحدة، مما يقلل من تكرار الكود.
استخدم النمط لعزل منطق الأعمال لفئة ما عن تفاصيل تنفيذ الخوارزميات التي قد لا تكون بهذه الأهمية في سياق ذلك المنطق.
يتيح لك نمط الاستراتيجية عزل الكود والبيانات الداخلية والتبعيات لخوارزميات مختلفة عن بقية الكود. يحصل العملاء المختلفون على واجهة بسيطة لتنفيذ الخوارزميات والتبديل بينها أثناء وقت التشغيل.
استخدم النمط عندما تحتوي فئتك على جملة شرطية ضخمة تتبدل بين متغيرات مختلفة من نفس الخوارزمية.
يتيح لك نمط الاستراتيجية التخلص من مثل هذه الجملة الشرطية عن طريق استخراج جميع الخوارزميات إلى فئات منفصلة، وجميعها تُنفّذ نفس الواجهة. يفوّض الكائن الأصلي التنفيذ إلى أحد هذه الكائنات، بدلاً من تنفيذ جميع متغيرات الخوارزمية.
##How to Implement
-
في فئة السياق، حدِّد خوارزمية عرضة للتغيير المتكرر. قد تكون أيضاً جملة شرطية ضخمة تختار وتُنفّذ متغيراً من نفس الخوارزمية أثناء وقت التشغيل.
-
أعلن عن واجهة الاستراتيجية المشتركة لجميع متغيرات الخوارزمية.
-
استخرج جميع الخوارزميات واحدةً تلو الأخرى إلى فئاتها الخاصة. يجب أن تُنفّذ جميعها واجهة الاستراتيجية.
-
في فئة السياق، أضف حقلاً لتخزين مرجع إلى كائن استراتيجية. وفّر دالة ضبط لاستبدال قيم ذلك الحقل. يجب أن يعمل السياق مع كائن الاستراتيجية فقط عبر واجهة الاستراتيجية. يجوز للسياق تعريف واجهة تُتيح للاستراتيجية الوصول إلى بياناته.
-
يجب على عملاء السياق ربطه باستراتيجية مناسبة تتطابق مع الطريقة التي يتوقعون بها من السياق أداء مهمته الأساسية.
##Pros & Cons
- يمكنك تبادل الخوارزميات المستخدمة داخل كائن ما أثناء وقت التشغيل.
- يمكنك عزل تفاصيل تنفيذ الخوارزمية عن الكود الذي يستخدمها.
- يمكنك استبدال الوراثة بالتركيب.
- مبدأ الفتح/الإغلاق. يمكنك إدخال استراتيجيات جديدة دون الحاجة إلى تغيير السياق.
- إذا كان لديك خوارزميتان فقط ونادراً ما تتغيران، فلا يوجد سبب حقيقي لتعقيد البرنامج بفئات وواجهات جديدة تأتي مع النمط.
- يجب أن يكون العملاء على دراية بالفروق بين الاستراتيجيات لتمكينهم من اختيار الاستراتيجية المناسبة.
- تدعم كثير من لغات البرمجة الحديثة الأنواع الوظيفية (functional types) مما يتيح لك تنفيذ إصدارات مختلفة من خوارزمية داخل مجموعة من الدوال المجهولة. يمكنك حينها استخدام هذه الدوال بالضبط كما كنت ستستخدم كائنات الاستراتيجية، لكن دون إثقال كودك بفئات وواجهات إضافية.
##Relations with Other Patterns
-
تمتلك أنماط Bridge و State و Strategy (وبدرجة ما Adapter) هياكل متشابهة جداً. في الواقع، تعتمد جميع هذه الأنماط على التركيب، أي تفويض العمل إلى كائنات أخرى. غير أنها تحل مشاكل مختلفة. النمط ليس مجرد وصفة لهيكلة كودك بطريقة معينة. يمكنه أيضاً إيصال للمطورين الآخرين المشكلة التي يحلها النمط.
-
قد يبدو Command و Strategy متشابهَيْن لأنه يمكنك استخدام كليهما لتحديد معامل كائن بإجراء ما. غير أن لديهما نوايا مختلفة جداً.
-
يمكنك استخدام Command لتحويل أي عملية إلى كائن. تصبح معاملات العملية حقولاً لذلك الكائن. يتيح لك التحويل تأجيل تنفيذ العملية، ووضعها في قائمة انتظار، وتخزين سجل الأوامر، وإرسال الأوامر إلى خدمات بعيدة، وما إلى ذلك.
-
من ناحية أخرى، يصف Strategy عادةً طرقاً مختلفة لفعل الشيء ذاته، مما يتيح لك تبادل هذه الخوارزميات داخل فئة سياق واحدة.
-
-
يتيح لك Decorator تغيير مظهر الكائن، بينما يتيح لك Strategy تغيير جوهره.
-
يعتمد Template Method على الوراثة: يتيح لك تغيير أجزاء من خوارزمية عن طريق توسيع تلك الأجزاء في الفئات الفرعية. يعتمد Strategy على التركيب: يمكنك تغيير أجزاء من سلوك الكائن عن طريق تزويده باستراتيجيات مختلفة تتوافق مع ذلك السلوك. يعمل Template Method على مستوى الفئة، لذا فهو ثابت. يعمل Strategy على مستوى الكائن، مما يتيح لك التبديل بين السلوكيات أثناء وقت التشغيل.
-
يمكن اعتبار State امتداداً لـ Strategy. يعتمد كلا النمطين على التركيب: يغيران سلوك السياق عن طريق تفويض بعض العمل إلى كائنات مساعدة. يجعل Strategy هذه الكائنات مستقلة تماماً وغير مدركة لبعضها. غير أن State لا يُقيّد التبعيات بين الحالات المحددة، مما يتيح لها تغيير حالة السياق حسب الرغبة.