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

نمط الاستراتيجية (Strategy Pattern)

جزء من قسم Object Oriented Programming في رحلة C# على Coddy — الدرس 55 من 70.

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

يتكون النمط من ثلاثة أجزاء: واجهة استراتيجية تحدد عقد الخوارزمية، وفئات استراتيجية ملموسة تنفذ سلوكيات مختلفة، وفئة سياق تستخدم استراتيجية:

public interface IPaymentStrategy
{
    void Pay(decimal amount);
}

public class CreditCardPayment : IPaymentStrategy
{
    public void Pay(decimal amount) => 
        Console.WriteLine($"Paid {amount} via Credit Card");
}

public class PayPalPayment : IPaymentStrategy
{
    public void Pay(decimal amount) => 
        Console.WriteLine($"Paid {amount} via PayPal");
}

public class ShoppingCart
{
    private IPaymentStrategy _paymentStrategy;
    
    public void SetPaymentMethod(IPaymentStrategy strategy)
    {
        _paymentStrategy = strategy;
    }
    
    public void Checkout(decimal total)
    {
        _paymentStrategy.Pay(total);
    }
}

لا يعرف السياق (ShoppingCart) أي وسيلة دفع يستخدمها - هو فقط يستدعي Pay() على أي استراتيجية تم توفيرها:

var cart = new ShoppingCart();

cart.SetPaymentMethod(new CreditCardPayment());
cart.Checkout(99.99m);  // تم دفع 99.99 عبر بطاقة الائتمان

cart.SetPaymentMethod(new PayPalPayment());
cart.Checkout(49.99m);  // تم دفع 49.99 عبر PayPal

يقضي نمط الاستراتيجية (Strategy pattern) على المنطق الشرطي المعقد. إضافة وسيلة دفع جديدة تعني إنشاء فئة (class) جديدة تنفذ IPaymentStrategy - لا حاجة لإجراء تغييرات في ShoppingCart. هذا يجعل الكود الخاص بك مفتوحاً للتوسيع ولكن مغلقاً للتعديل.

challenge icon

التحدي

سهل

لنقم ببناء آلة حاسبة لتكلفة الشحن باستخدام نمط الاستراتيجية (Strategy pattern). طرق الشحن المختلفة لها خوارزميات تسعير مختلفة، ويجب أن يسمح نظامك بالتبديل بينها في وقت التشغيل دون تغيير منطق معالجة الطلبات الأساسي.

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

  • ShippingStrategy.cs: قم بتعريف واجهة IShippingStrategy في مساحة الأسماء Shipping مع طريقة CalculateCost(double weight) التي ترجع double. ثم قم بإنشاء ثلاث فئات استراتيجية ملموسة:

    • StandardShipping - تحسب التكلفة كـ weight * 1.5
    • ExpressShipping - تحسب التكلفة كـ weight * 3.0 + 5.0 (تتضمن رسوم شحن سريع ثابتة)
    • OvernightShipping - تحسب التكلفة كـ weight * 5.0 + 10.0 (خدمة مميزة مع رسوم أساسية أعلى)
  • OrderProcessor.cs: قم بإنشاء فئة OrderProcessor في نفس مساحة الأسماء. هذه هي فئة السياق (context class) التي تستخدم استراتيجية الشحن. يجب أن تحتوي على:
    • حقل خاص للاحتفاظ بـ IShippingStrategy الحالي
    • طريقة SetShippingMethod(IShippingStrategy strategy) لتغيير استراتيجية الشحن
    • طريقة ProcessOrder(double weight) تستخدم الاستراتيجية الحالية لحساب وإرجاع تكلفة الشحن
  • Program.cs: اجمع كل شيء معاً عن طريق إنشاء OrderProcessor وتوضيح كيف يمكنك تبديل استراتيجيات الشحن في وقت التشغيل. يمكن لنفس المعالج حساب التكاليف باستخدام خوارزميات مختلفة ببساطة عن طريق تغيير استراتيجيته.

ستتلقى مدخلين:

  • نوع طريقة الشحن: standard، أو express، أو overnight
  • وزن الطرد كرقم عشري (على سبيل المثال، 4.5)

قم بإنشاء OrderProcessor، وضبط استراتيجية الشحن المناسبة بناءً على نوع الإدخال، ثم قم بمعالجة الطلب واطبع التكلفة المحسوبة.

على سبيل المثال، إذا كانت المدخلات هي express و 4.5، فيجب أن يكون الناتج:

18.5

يوضح هذا قوة نمط الاستراتيجية - حيث لا يحتوي OrderProcessor الخاص بك على أي منطق لحساب الشحن بنفسه. إنه ببساطة يفوض المهمة لأي استراتيجية تم إعطاؤها له، مما يجعل من السهل إضافة طرق شحن جديدة (مثل DroneDelivery) دون تعديل المعالج على الإطلاق!

ورقة مرجعية

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

يتكون النمط من ثلاثة أجزاء:

  • واجهة الاستراتيجية (Strategy interface): تحدد عقد الخوارزمية
  • فئات الاستراتيجية الملموسة (Concrete strategy classes): تنفذ سلوكيات مختلفة
  • فئة السياق (Context class): تستخدم استراتيجية دون معرفة تفاصيل تنفيذها

هيكل المثال:

// واجهة الاستراتيجية
public interface IPaymentStrategy
{
    void Pay(decimal amount);
}

// الاستراتيجيات الملموسة
public class CreditCardPayment : IPaymentStrategy
{
    public void Pay(decimal amount) => 
        Console.WriteLine($"Paid {amount} via Credit Card");
}

public class PayPalPayment : IPaymentStrategy
{
    public void Pay(decimal amount) => 
        Console.WriteLine($"Paid {amount} via PayPal");
}

// فئة السياق
public class ShoppingCart
{
    private IPaymentStrategy _paymentStrategy;
    
    public void SetPaymentMethod(IPaymentStrategy strategy)
    {
        _paymentStrategy = strategy;
    }
    
    public void Checkout(decimal total)
    {
        _paymentStrategy.Pay(total);
    }
}

الاستخدام - تبديل الاستراتيجيات في وقت التشغيل:

var cart = new ShoppingCart();

cart.SetPaymentMethod(new CreditCardPayment());
cart.Checkout(99.99m);  // Paid 99.99 via Credit Card

cart.SetPaymentMethod(new PayPalPayment());
cart.Checkout(49.99m);  // Paid 49.99 via PayPal

الفوائد:

  • يلغي المنطق الشرطي المعقد
  • مفتوح للتوسيع، مغلق للتعديل
  • إضافة استراتيجيات جديدة تتطلب فقط إنشاء فئات جديدة، دون تغييرات في الكود الحالي

جرّب بنفسك

using System;
using Shipping;

class Program
{
    public static void Main(string[] args)
    {
        // قراءة المدخلات
        string shippingType = Console.ReadLine();
        double weight = Convert.ToDouble(Console.ReadLine());

        // TODO: إنشاء مثيل من OrderProcessor

        // TODO: بناءً على shippingType ("standard" أو "express" أو "overnight")،
        // إنشاء استراتيجية الشحن المناسبة وتعيينها في الـ processor

        // TODO: معالجة الطلب وطباعة التكلفة المحسوبة
    }
}
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming