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

نمط Strategy

جزء من قسم Object Oriented Programming في رحلة Dart على Coddy — الدرس 93 من 110.

يتيح لك نمط الاستراتيجية (Strategy pattern) تعريف مجموعة من الخوارزميات، وتغليف كل واحدة منها في فئة (class) خاصة بها، وجعلها قابلة للتبديل في وقت التشغيل (runtime). فبدلاً من كتابة السلوك بشكل ثابت داخل الفئة، تقوم بحقن السلوك من خلال واجهة (interface)، مما يسمح لك بتبديل الاستراتيجيات دون تعديل الفئة التي تستخدمها.

فكر في نظام دفع حيث تريد دعم طرق دفع مختلفة. وبدلاً من استخدام الجمل الشرطية للتعامل مع كل طريقة، يمكنك تحديد واجهة استراتيجية (strategy interface) وإنشاء تطبيقات ملموسة (concrete implementations):

abstract class PaymentStrategy {
  void pay(double amount);
}

class CreditCardPayment implements PaymentStrategy {
  @override
  void pay(double amount) => print('Paid \$amount with Credit Card');
}

class PayPalPayment implements PaymentStrategy {
  @override
  void pay(double amount) => print('Paid \$amount with PayPal');
}

class ShoppingCart {
  PaymentStrategy _paymentStrategy;

  ShoppingCart(this._paymentStrategy);

  void setPaymentStrategy(PaymentStrategy strategy) {
    _paymentStrategy = strategy;
  }

  void checkout(double total) {
    _paymentStrategy.pay(total);
  }
}

void main() {
  var cart = ShoppingCart(CreditCardPayment());
  cart.checkout(99.99);  // تم دفع $99.99 باستخدام بطاقة الائتمان

  cart.setPaymentStrategy(PayPalPayment());
  cart.checkout(49.99);  // تم دفع $49.99 باستخدام PayPal
}

لا يعرف ShoppingCart أو يهتم بكيفية عمل الدفع - فهو يقوم فقط بالتفويض إلى أي استراتيجية (strategy) محددة حاليًا. يمكنك إضافة طرق دفع جديدة عن طريق إنشاء فئات استراتيجية (strategy classes) جديدة دون المساس بكود سلة التسوق. هذا النمط مثالي عندما يكون لديك طرق متعددة لتنفيذ إجراء ما وتريد الاختيار أو التبديل بينها ديناميكيًا.

challenge icon

التحدي

سهل

لنقم ببناء نظام لتنسيق النصوص باستخدام نمط الاستراتيجية (Strategy pattern)! ستقوم بإنشاء نظام حيث يمكن تطبيق استراتيجيات تنسيق مختلفة على النص، مما يسمح للمستخدمين بالتبديل بين التنسيقات ديناميكيًا دون تغيير معالج النصوص الأساسي.

ستقوم بتنظيم الكود الخاص بك في ملفين:

  • formatting_strategies.dart: أنشئ نظام الاستراتيجية الخاص بك هنا. قم بتعريف فئة مجردة (abstract class) باسم FormattingStrategy مع طريقة String format(String text). ثم قم بتنفيذ ثلاث استراتيجيات ملموسة:

    • UppercaseStrategy - يحول النص إلى أحرف كبيرة بالكامل
    • LowercaseStrategy - يحول النص إلى أحرف صغيرة بالكامل
    • TitleCaseStrategy - يجعل الحرف الأول من كل كلمة كبيرًا (يتم التقسيم بواسطة المسافات، مع جعل الحرف الأول من كل كلمة كبيرًا، والباقي صغيرًا)
    قم أيضًا بإنشاء فئة TextProcessor تحتفظ بـ FormattingStrategy. يجب أن تحتوي على منشئ (constructor) يقبل استراتيجية أولية، وطريقة setStrategy() لتغيير الاستراتيجية أثناء وقت التشغيل، وطريقة process(String text) التي تطبق الاستراتيجية الحالية وتعيد النتيجة المنسقة.
  • main.dart: استورد استراتيجيات التنسيق الخاصة بك ووضح كيف يسمح نمط الاستراتيجية بتبديل السلوك ديناميكيًا. أنشئ TextProcessor يبدأ بـ UppercaseStrategy. قم بمعالجة النص hello world واطبع النتيجة. ثم انتقل إلى LowercaseStrategy، وقم بمعالجة HELLO WORLD، واطبع النتيجة. أخيرًا، انتقل إلى TitleCaseStrategy، وقم بمعالجة the quick brown fox، واطبع النتيجة.

يوضح هذا قوة نمط الاستراتيجية - لا يحتاج TextProcessor الخاص بك إلى معرفة تفاصيل كيفية عمل التنسيق. إنه ببساطة يفوض المهمة إلى أي استراتيجية محددة حاليًا، مما يسهل إضافة خيارات تنسيق جديدة دون تعديل المعالج!

المخرجات المتوقعة:

HELLO WORLD
hello world
The Quick Brown Fox

ورقة مرجعية

يحدد نمط الاستراتيجية (Strategy pattern) عائلة من الخوارزميات، ويغلف كل واحدة منها في فئة (class) خاصة بها، ويجعلها قابلة للتبديل في وقت التشغيل. بدلاً من كتابة السلوك بشكل ثابت، تقوم بحقنه من خلال واجهة (interface).

قم بتعريف واجهة الاستراتيجية:

abstract class PaymentStrategy {
  void pay(double amount);
}

قم بإنشاء تطبيقات استراتيجية ملموسة:

class CreditCardPayment implements PaymentStrategy {
  @override
  void pay(double amount) => print('Paid \$amount with Credit Card');
}

class PayPalPayment implements PaymentStrategy {
  @override
  void pay(double amount) => print('Paid \$amount with PayPal');
}

قم بإنشاء فئة سياق (context class) تستخدم الاستراتيجية:

class ShoppingCart {
  PaymentStrategy _paymentStrategy;

  ShoppingCart(this._paymentStrategy);

  void setPaymentStrategy(PaymentStrategy strategy) {
    _paymentStrategy = strategy;
  }

  void checkout(double total) {
    _paymentStrategy.pay(total);
  }
}

استخدم النمط عن طريق حقن وتبديل الاستراتيجيات:

var cart = ShoppingCart(CreditCardPayment());
cart.checkout(99.99);  // تم دفع $99.99 بواسطة بطاقة الائتمان

cart.setPaymentStrategy(PayPalPayment());
cart.checkout(49.99);  // تم دفع $49.99 بواسطة PayPal

تقوم فئة السياق بتفويض السلوك إلى الاستراتيجية دون معرفة تفاصيل التنفيذ. أضف استراتيجيات جديدة عن طريق إنشاء فئات جديدة دون تعديل الكود الحالي.

جرّب بنفسك

import 'formatting_strategies.dart';

void main() {
  // TODO: إنشاء TextProcessor يبدأ بـ UppercaseStrategy
  
  // TODO: معالجة النص 'hello world' وطباعة النتيجة
  
  // TODO: التبديل إلى LowercaseStrategy
  
  // TODO: معالجة 'HELLO WORLD' وطباعة النتيجة
  
  // TODO: التبديل إلى TitleCaseStrategy
  
  // TODO: معالجة 'the quick brown fox' وطباعة النتيجة
}
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming