Strategy Pattern خبير الاستراتيجيات الذكي
السلام عليكم ورحمة الله وبركاته
المقدمة
اليوم سنتعلم كيف نصمم نظامًا ذكيًا يتكيف مع التغييرات بسهولة وسلاسة دون الحاجة لتعديل الكود الأساسي بشكل متكرر
وسنفعل ذلك باستخدام الـ Strategy Pattern وهو أحد الـ Design Patterns وهو يندرج تحت عائلة الـ Behavioral Design Patterns
والـ Behavioral Design Patterns بشكل عام تهتم بكيفية تفاعل الكلاسات مع بعضها البعض وكيفية تبادل البيانات بينها بشكل سلس
والـ Strategy Pattern هوأحد تلك الطرق التي تساعدنا على تحقيق ذلك
في هذا المقال، سنستعرض كيف يمكننا استخدام الـ Strategy Pattern لجعل تطبيقاتنا أكثر مرونة وقابلية للتوسع
والـ Strategy Pattern يساعدنا على الاختيار والتبديل السلس بين الخوارزميات سواء كانت في الدوال أو الكلاسات المختلفة في اثناء الـ Runtime
وسنرى هذا في الأمثلة العملية التي سنقوم بها في هذا المقال
لكن بالطبع قبل أن نبدأ في شرح الـ Strategy Pattern، دعونا نتعرف على المشكلة التي يقوم بحلها
المشكلة التي يحلها الـ Strategy Pattern
تخيل أنك مطالب بارسال بعض التقارير عن النظام
وكل تقرير يقوم بارسال بيانات مختلفة واحصائيات مختلفة
فقد يكون لديك تقارير مثل:
- تقرير عن المبيعات الشهرية
- تقرير عن الزبائن الجدد
- تقرير عن المنتجات الأكثر مبيعًا
- تقرير عن الشحنات المتأخرة
- تقرير عن الشحنات التي تم تسليمها
- تقرير عن الشحنات التي لم يتم تسليمها
- تقرير عن الشحنات التي تم إلغاؤها
- ... إلخ
تقارير كثيرة وكل تقرير له طريقة مختلفة في جمع البيانات وحسابها بشكل مختلف
فقد يكون لديك كلاس يدير عملية جمع البيانات، ستسميه ReportGenerator والذي سيحتوي على دالة generate التي تأخذ نوع التقرير
class ReportGenerator {
generate(reportType: string): void {
if (reportType === 'monthlySales') {
// جلب بيانات المبيعات من قاعدة البيانات
// حساب إجمالي المبيعات والأرباح
// إنشاء التقرير وعرضه
} else if (reportType === 'newCustomers') {
// جلب بيانات الزبائن الجدد من قاعدة البيانات
// حساب عدد الزبائن الجدد في الشهر
// إنشاء التقرير وعرضه
} else if (reportType === 'mostSoldProducts') {
// جلب بيانات المبيعات لكل منتج
// ترتيب المنتجات حسب كمية المبيعات
// إنشاء تقرير بالمنتجات الأكثر مبيعًا
} else if (reportType === 'delayedShipments') {
// جمع بيانات الشحنات المتأخرة
// تحليل أسباب التأخير
// إنشاء تقرير بالإجراءات المطلوبة
} else if (reportType === 'deliveredShipments') {
// جمع بيانات الشحنات التي تم تسليمها
// حساب نسبة الشحنات التي تم تسليمها بنجاح
// إنشاء تقرير بالشحنات التي تم تسليمها
} else if (reportType === 'undeliveredShipments') {
// جمع بيانات الشحنات التي لم يتم تسليمها
// تحليل أسباب عدم التسليم
// إنشاء تقرير بالشحنات التي لم يتم تسليمها
} else if (reportType === 'canceledShipments') {
// جمع بيانات الشحنات التي تم إلغاؤها
// تحليل أسباب الإلغاء
// إنشاء تقرير بالشحنات التي تم إلغاؤها
} else {
throw new Error('Invalid report type');
}
}
}
هل يمكنك تخيل المشاكل التي ستواجهها هنا؟
أولًا ستقول لي أن هناك الكثير من الأكواد المختلفة في دالة واحدة وهذاالكثير من المباديء البرمجية
مثل مبدأ Single Responsibility Principle الذي ينص على أن كل كلاس أو دالة يجب أن تكون مسؤولة عن شيء واحد فقط
أما هنا سترى أن عدة عمليات مختلفة عن بعضها البعض مثل generate monthly sales و generate new customers و generate most sold products كلها موجودة في دالة واحدة وهذا يتعارض مع مبدأ Single Responsibility Principle
وأيضًا الدالة تتعارض مع مبدأ Open/Closed Principle الذي ينص على أن الكلاسات يجب أن تكون مفتوحة للتوسع ومغلقة للتعديل
أما هنا فكل شيء في دالة واحدة وفي كلاس واحد
فلو أردنا إضافة أو حذف أو تعديل أي تقرير، سنضطر لتعديل الدالة generate في كل مرة
والدالة generate ستصبح أكبر وأكبر مع مرور لذا التعديل فيها سيصبح صعبًا جدًا لأن هناك الكثير من الأكواد المختلفة والمتداخلة في دالة واحدة
وتعديل شيء واحد قد يجعلك دون قصد تفسد شيء آخر، فبدلًا من أن تعدل تقرير واحد ستجد نفسك قد أفسدت أكواد أخرى في التقارير الأخرى دون أن تدري
يمكننا ان نلخص المشاكل التي سنواجهها في النقاط التالية:
- عدم الالتزام بمبدأ
Single Responsibility Principleبحيث أن الدالةgenerateتقوم بكل شيء من جلب بيانات وتمعالجتها وعرضها وتقوم بهذا لعدة تقارير مختلفة - عدم الالتزام بمبدأ
Open/Closed Principleبحيث أنك تضطر لتعديل الدالةgenerateفي كل مرة تريد فيها إضافة أو حذف أو تعديل أي تقرير - زيادة تعقيد الدالة
generateمع مرور الوقت، مما يجعل من الصعب فهمها وتعديلها - زيادة فرص حدوث الأخطاء، حيث أن أي تعديل في الدالة
generateقد يؤثر على التقارير الأخرى دون قصد - ... إلخ
هنا بالطبع لا داعي لأن أخبركم أن من سيحل هذه المشكلة هو بالطبع بطلنا المغوار وخبير الاستراتيجيات الذكي الـ Strategy Pattern
الذي سيساعدنا على فصل كل خوارزمية أو سلوك مختلف في كلاس منفصل، مما يجعل من السهل علينا إضافة أو حذف أو تعديل أي خوارزمية دون التأثير على الكلاسات الأخرى، وهو يعد تطبيق عملي ومباشر لمبدأ Open/Closed Principle
بطبع كل مباديء الـ SOLID شرحناها في مقالة مبادئ الـ SOLID لكتابة كود قوي وصلب كالحديد
شرحنا فيها كل مبدأ من مباديء الـ SOLID وكيفية تطبيقه في الكود بأمثلة عملية
ما هو الـ Strategy Pattern ؟
هو ببساطة طريقة لتقسيم الخوارزميات والأكواد المختلفة في كلاسات منفصلة، بحيث يمكننا استبدالها والتبديل بين تلك الخوارزميات بسهولة ودون الحاجة لتعديل الكود الأساسي
وهو ببساطة يتكون من 3 مكونات رئيسية:
Strategy Interface: الـInterfaceالأساسي الذي سيكون الشكل العام لكل كلاس من الاستراتيجيات المختلفة
وفي حالتنا سيكون لديناReportStrategyالذي سيحتوي على دالةgenerateالتي ستقوم بجلب البيانات ومعالجتها وعرضها
أو يمكننا أن نقسمها إلى3دوال مختلفة، بحيث تكون لدينا دالةfetchDataلجلب البيانات ودالةprocessDataلمعالجة البيانات ودالةdisplayDataلعرض البيانات
ويمكننا جعل الدالةgenerateتقوم باستدعاء تلك الدوال الثلاثة على التواليConcrete Strategies: الكلاسات التي تنفذ الـInterfaceوتقوم بتطبيق الخوارزمية المختلفة
وفي حالتنا كل كلاس سيهتم بتنفيذ عملية واحدة، بالتالي سيكون لدينا:NewCustomersReportStrategyMonthlySalesReportStrategyMostSoldProductsReportStrategyDelayedShipmentsReportStrategyDeliveredShipmentsReportStrategy- ... إلخ
وكل كلاس سيقوم بتنفيذ دالة
generateالخاصة به والتي ستقوم بجلب البيانات ومعالجتها وعرضها
Context: الكلاس الذي يستخدم الاستراتيجية المختارة
وهو الكلاس الأساسي الذي سيختار الاستراتيجية المناسبة ويقوم باستدعاء الدالةgenerateالخاصة
ويستطيع التبديل بين الاستراتيجيات المختلفة في أي وقت، مما يجعل من السهل علينا إضافة أو حذف أو تعديل أي استراتيجية دون التأثير على الكلاسات الأخرى
التطبيق العملي للـ Strategy Pattern
الآن بعد أن فهمنا المكونات الأساسية للـ Strategy Pattern، دعونا نطبقها على مثال التقارير الذي ذكرناه سابقًا
إنشاء الـ Strategy Interface
أولًا سنقوم بإنشاء الـ Interface الأساسي الذي ستنفذه كل الاستراتيجيات الخاصة بالتقارير
لنسميه IReportStrategy، وهو سيكون الشكل العام لكل كلاس من الاستراتيجيات المختلفة الخاصة بكل تقرير
interface IReportStrategy {
generate(): void;
}
هنا قمنا بعمل interface بسيط يحتوي على دالة generate فقط للتبسيط، لكن يمكنك تقسيمها إلى عدة دوال كما ذكرنا سابقًا
فيمكنك جعلها تحتوي على دالة fetchData لجلب البيانات ودالة processData لمعالجة البيانات ودالة displayData لعرض البيانات
لكن من أجل التبسيط، سنتركها كما هي الآن تحتوي على دالة generate فقط
إنشاء الـ Concrete Strategies
الآن سنقوم بإنشاء الكلاسات التي تنفذ الـ Interface وكل كلاس سيهتم بنوع تقرير معين
والكلاسات التي ستنفذ الـ IReportStrategy نسميها Concrete Strategies
ومن ضمن الكلاسات التي سنقوم بإنشائها هي هي NewCustomersReportStrategy و MonthlySalesReportStrategy و MostSoldProductsReportStrategy و DelayedShipmentsReportStrategy وغيرها حسب الحاجة
class NewCustomersReportStrategy implements IReportStrategy {
public generate(): void {
console.log('جاري جمع بيانات العملاء الجدد...');
// جلب بيانات العملاء الجدد من قاعدة البيانات
// معالجة البيانات وحساب الإحصائيات
// عرض التقرير أو إرساله
console.log('تم إنشاء تقرير العملاء الجدد بنجاح');
}
}
class MonthlySalesReportStrategy implements IReportStrategy {
public generate(): void {
console.log('جاري جمع بيانات المبيعات الشهرية...');
// جلب بيانات المبيعات من قاعدة البيانات
// حساب إجمالي المبيعات والأرباح
// إنشاء المخططات والرسوم البيانية
console.log('تم إنشاء تقرير المبيعات الشهرية بنجاح');
}
}
class MostSoldProductsReportStrategy implements IReportStrategy {
public generate(): void {
console.log('جاري تحليل بيانات المنتجات الأكثر مبيعًا...');
// جلب بيانات المبيعات لكل منتج
// ترتيب المنتجات حسب كمية المبيعات
// إنشاء قائمة بالمنتجات الأكثر مبيعًا
console.log('تم إنشاء تقرير المنتجات الأكثر مبيعًا بنجاح');
}
}
class DelayedShipmentsReportStrategy implements IReportStrategy {
public generate(): void {
console.log('جاري تحليل بيانات الشحنات المتأخرة...');
// جلب بيانات الشحنات المتأخرة
// تحليل أسباب التأخير
// إنشاء تقرير بالإجراءات المطلوبة
console.log('تم إنشاء تقرير الشحنات المتأخرة بنجاح');
}
}
لاحظ أن كل كلاس يركز على مهمة واحدة فقط وهي إنشاء نوع معين من التقارير
وكل كلاس يحتوي على المنطق والكود الخاص به لجمع ومعالجة وعرض البيانات
وهذا يتماشى مع مبدأ Single Responsibility Principle حيث أن كل كلاس مسؤول عن شيء واحد فقط
وأيضًا يتماشى مع مبدأ Open/Closed Principle حيث يمكنك إضافة كلاس جديد لتقرير جديد وجعله ينفذ IReportStrategy دون الحاجة لتعديل الكود الموجود
إنشاء الـ Context
الآن سنقوم بإنشاء الكلاس الذي سيستخدم الاستراتيجيات المختلفة وهذا الكلام يسمى Context وهو الكلاس الاساسي الذي سيختار الاستراتيجية المناسبة ويقوم باستدعاء الدالة generate الخاصة بها
لنسميه ReportGenerator، وهو سيكون الكلاس الذي يدير عملية إنشاء التقارير
ويحتوي على دالة setStrategy لتغيير الاستراتيجية في أي وقت
ودالة generateReport التي تستدعي الدالة generate من الاستراتيجية الحالية
class ReportGenerator {
private strategy: IReportStrategy;
constructor(strategy: IReportStrategy) {
this.strategy = strategy;
}
public setStrategy(strategy: IReportStrategy): void {
this.strategy = strategy;
}
public generateReport(): void {
this.strategy.generate();
}
}
هنا قمنا بإنشاء كلاس ReportGenerator الذي ستلاحظ عدة أشياء مهمة فيه:
- يحتوي على
strategyمن نوعIReportStrategy- هنا سنستخدمه ونستفيد من فكرة الـ
Polymorphismحيث أن كل كلاس من الاستراتيجيات المختلفة ينفذ نفس الـInterface
لذا فالمتغيرstrategyيمكن أن يكون من أي نوع من الاستراتيجيات التي تنفذIReportStrategy
ولاحظ أننا هنا نتعامل معinterfaceوهوIReportStrategyولا نهتم بنوع الاستراتيجية المستخدمة حاليًا وهذا هو مفهوم الـAbstracting
- هنا سنستخدمه ونستفيد من فكرة الـ
- يمكنه استقبال الاستراتيجية من خلال الـ
constructorلتحديد الاستراتيجية الافتراضية عند إنشاءobjectمنReportGeneratorلكن يمكننا أيضًا تغييرها لاحقًا - يحتوي على دالة
setStrategyلتغيير الاستراتيجية في أي وقت وهنا تكمن المرونة في استخدام الـStrategy Patternحيث يمكنك تغيير الاستراتيجية في أي وقت حسب الحاجة - يحتوي على دالة
generateReportالتي تستدعي الدالةgenerateمن الاستراتيجية الحالية وبما أن كل الاستراتيجيات تنفذ نفس الـInterface، فيمكننا استدعاءgenerateدون الحاجة لمعرفة نوع الاستراتيجية المستخدمة حاليًا
استخدام الـ Strategy Pattern
الآن يمكننا استخدام الـ Strategy Pattern بسهولة
// إنشاء تقرير العملاء الجدد
const newCustomersStrategy = new NewCustomersReportStrategy();
const reportGenerator = new ReportGenerator(newCustomersStrategy);
reportGenerator.generateReport();
// تغيير الاستراتيجية لإنشاء تقرير المبيعات الشهرية
const monthlySalesStrategy = new MonthlySalesReportStrategy();
reportGenerator.setStrategy(monthlySalesStrategy);
reportGenerator.generateReport();
// تغيير الاستراتيجية لإنشاء تقرير المنتجات الأكثر مبيعًا
const mostSoldProductsStrategy = new MostSoldProductsReportStrategy();
reportGenerator.setStrategy(mostSoldProductsStrategy);
reportGenerator.generateReport();
// تغيير الاستراتيجية لإنشاء تقرير الشحنات المتأخرة
const delayedShipmentsStrategy = new DelayedShipmentsReportStrategy();
reportGenerator.setStrategy(delayedShipmentsStrategy);
reportGenerator.generateReport();
هنا قمنا بإنشاء ReportGenerator مع استراتيجية NewCustomersReportStrategy، ثم قمنا بتوليد التقرير الخاص بالعملاء الجدد
ثم لاحقًا قمنا بتغيير الاستراتيجية إلى MonthlySalesReportStrategy وتوليد تقرير المبيعات الشهرية
ثم قمنا بتغيير الاستراتيجية مرة أخرى إلى MostSoldProductsReportStrategy وتوليد تقرير المنتجات الأكثر مبيعًا
شكل آخر للـ Context
يمكنك أيضًا استخدام دالة بسيطة كـ Context بدلًا من كلاس
فالـ Context ليس شرطًا أن يكون كلاس، بل يمكن أن يكون دالة بسيطة تقوم بأخذ enum يمثل نوع التقرير وتقوم بتحديد الاستراتيجية المناسبة بناءً على ذلك
enum ReportType {
NewCustomers,
MonthlySales,
MostSoldProducts,
DelayedShipments,
}
function generateReport(reportType: ReportType): void {
let strategy: IReportStrategy;
switch (reportType) {
case ReportType.NewCustomers:
strategy = new NewCustomersReportStrategy();
break;
case ReportType.MonthlySales:
strategy = new MonthlySalesReportStrategy();
break;
case ReportType.MostSoldProducts:
strategy = new MostSoldProductsReportStrategy();
break;
case ReportType.DelayedShipments:
strategy = new DelayedShipmentsReportStrategy();
break;
default:
throw new Error('نوع التقرير غير مدعوم');
}
strategy.generate();
}
هنا قمنا بإنشاء دالة generateReport التي تأخذ enum يمثل نوع التقرير وتقوم بتحديد الاستراتيجية المناسبة بناءً على ذلك
داخل الدالة لدينا switch-case يحدد أي استراتيجية يجب استخدامها بناءً على نوع التقرير المرسل
ثم ببساطة نستدعي الدالة generate من الاستراتيجية المحددة
وطريقة الاستخدام ستكون كالتالي:
// استخدام الدالة لتوليد تقرير العملاء الجدد
generateReport(ReportType.NewCustomers);
// استخدام الدالة لتوليد تقرير المبيعات الشهرية
generateReport(ReportType.MonthlySales);
// استخدام الدالة لتوليد تقرير المنتجات الأكثر مبيعًا
generateReport(ReportType.MostSoldProducts);
// استخدام الدالة لتوليد تقرير الشحنات المتأخرة
generateReport(ReportType.DelayedShipments);
بهذه الطريقة، يمكنك استخدام الـ Strategy Pattern لجعل الكود أكثر مرونة وقابلية للتوسع
ويمكنك بسهولة إضافة تقارير جديدة دون الحاجة لتعديل الكود الموجود، فقط قم بإنشاء كلاس جديد ينفذ IReportStrategy
ثم قم بتحديد الاستراتيجية الجديدة في الدالة generateReport أو في كلاس ReportGenerator يحسب الحاجة
فوائد الـ Strategy Pattern
دعونا نتذكر سريعًا الفوائد التي نحصل عليها من استخدام الـ Strategy Pattern في مثال التقارير الذي قمنا به:
تطبيق مبدأ Single Responsibility Principle
كل كلاس أصبح مسؤولًا عن شيء واحد فقط:
NewCustomersReportStrategyمسؤول فقط عن تقارير العملاء الجددMonthlySalesReportStrategyمسؤول فقط عن تقارير المبيعات الشهريةReportGeneratorمسؤول فقط عن إدارة الاستراتيجيات واستدعائها- ... إلخ
بالتالي عندما تريد تعديل تقرير معين، يمكنك الذهاب مباشرة إلى الكلاس الخاص به وتعديله دون الحاجة للقلق بشأن تأثير ذلك على التقارير الأخرى
وهذا يجعل الكود أكثر وضوحًا وسهولة في الفهم والتعديل وكل الكلمات الرنانة التي نسمعها
تطبيق مبدأ Open/Closed Principle
الكود أصبح:
- مفتوحًا للتوسع: يمكنك إضافة أنواع جديدة من التقارير بعمل كلاس جديد ينفذ
ReportStrategy - مغلقًا للتعديل: لا تحتاج لتعديل الكود الموجود عند إضافة تقارير جديدة، فمثلا لن تقوم بتعديل كلاس
ReportGeneratorأو الـIReportStrategyأو حتى الكلاسات الأخرى
بل فقط تضيف كلاس جديد للتقرير الجديد وتجعله ينفذIReportStrategy
// إضافة تقرير جديد بدون تعديل أي كود موجود
class InventoryReportStrategy implements ReportStrategy {
public generate(): void {
console.log('جاري تحليل بيانات المخزون...');
// الكود الخاص بالتقرير الجديد
console.log('تم إنشاء تقرير المخزون بنجاح');
}
}
// استخدام التقرير الجديد
reportGenerator.setStrategy(new InventoryReportStrategy());
reportGenerator.generateReport();
سهولة التبديل بين الاستراتيجيات
يمكن تغيير الاستراتيجية في أي وقت حسب الحاجة
باستخدام دالة setStrategy في كلاس ReportGenerator، يمكنك التبديل بين الاستراتيجيات المختلفة بسهولة ودون الحاجة لتعديل الكود الأساسي
وهذا يجعل الكود أكثر مرونة وقابلية للتوسع
const reportGenerator = new ReportGenerator(
new MonthlySalesReportStrategy()
);
// توليد تقرير المبيعات الشهرية
reportGenerator.generateReport();
// تغيير الاستراتيجية إلى تقرير جديد
reportGenerator.setStrategy(new NewCustomersReportStrategy());
reportGenerator.generateReport();
// تغيير الاستراتيجية إلى تقرير آخر
reportGenerator.setStrategy(new MostSoldProductsReportStrategy());
reportGenerator.generateReport();
// تغيير الاستراتيجية إلى تقرير آخر
reportGenerator.setStrategy(new DelayedShipmentsReportStrategy());
reportGenerator.generateReport();
لكن الشيء السلبي هنا في استخدام كلاس ReportGenerator هو أنك مجبر على انك وضع استراتيجية افتراضية عند إنشاء object من ReportGenerator لأول مرة
لأنك قد لا تحتاج إلى استراتيجية افتراضية في بعض الحالات خصوصًا لو كانت التقارير تعتمد على بيانات تلقيها من المستخدم أو من مصدر خارجي
لكن في هذه الحالة يمكنك استخدام الدالة generateReport التي قمنا بإنشائها سابقًا والتي تأخذ enum يمثل نوع التقرير وتقوم بتحديد الاستراتيجية المناسبة بناءً على ذلك بشكل ديناميكي
أول يمكنك ازالة الـ constructor من كلاس ReportGenerator وجعل الدالة setStrategy هي المسؤولة عن تعيين الاستراتيجية الحالية
وفي حالة أنك لم تقم بتعيين استراتيجية، يمكنك جعلها ترمي exception عند استدعاء generateReport أو تعيين استراتيجية افتراضية مثل DefaultReportStrategy التي تقوم بعرض رسالة تفيد بعدم تعيين استراتيجية
class ReportGenerator {
private strategy: IReportStrategy | null = null;
public setStrategy(strategy: IReportStrategy): void {
this.strategy = strategy;
}
public generateReport(): void {
if (!this.strategy) {
throw new Error('لم يتم تعيين استراتيجية للتقرير'); // أو يمكنك تعيين استراتيجية افتراضية هنا
}
this.strategy.generate();
}
}
الختام
الـ Strategy Pattern يعد من أقوى الـ Design Patterns التي تساعدك على كتابة كود سلس ومرن وقابل للتوسع بشكل كبير دون مشاكل أو تعقيدات
فهو يمكنك من:
- فصل الخوارزميات المختلفة في كلاسات منفصلة وهذا يتماشى مع مبدأ
Single Responsibility Principle - التبديل بين الخوارزميات أثناء الـ
Runtimeبسهولة وهو تطبيق مباشر لفكرة الـPolymorphismو الـAbstracting - إضافة خوارزميات جديدة بدون تعديل الكود الموجود لأنه يطبق مبدأ
Open/Closed Principle
كما رأينا في هذا المقال، الـ Strategy Pattern هو حل عملي لمشاكل حقيقية نواجهها في البرمجة بشكل يومي
فبدلًا من كتابة دوال طويلة مليئة بالـ if-else أو switch-case، يمكننا تنظيم الكود بطريقة أكثر احترافية ومرونة
أنصحك بتطبيق الـ Strategy Pattern في مشاريعك القادمة، وخصوصًا في الأجزاء التي تحتاج منك لتطبيق كود مختلف بحسب status أو action أو type معين
وستلاحظ كم سيصبح الكود أكثر سهولة في الفهم والتعديل وكل الكلام الرنان
وتذكر أن الهدف ليس استخدام الـ Design Patterns لمجرد الاستخدام، بل لحل المشاكل الفعلية وتحسين جودة الكود
لأنه أحيانًا قد يكون استخدام الـ Design Patterns غير ضروري أو حتى مضر إذا لم يكن هناك حاجة فعلية له وهذا حصل معي في بعض المشاريع التي عملت عليها
أتمنى أن يكون هذا المقال قد أعطى فكرة واضحة عن الـ Strategy Pattern وكيفية استخدامه في البرمجة