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

نمط Strategy

جزء من قسم البرمجة كائنية التوجه في رحلة PHP على Coddy. الدرس 74 من 91.

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

تخيّل cart يمكنه تطبيق استراتيجيات discount مختلفة. بدلاً من كتابة منطق discount بشكل ثابت باستخدام العبارات الشرطية، تقوم بتعريف كل discount كـ class منفصل:

<?php
interface DiscountStrategy {
    public function calculate(float $total): float;
}

class NoDiscount implements DiscountStrategy {
    public function calculate(float $total): float {
        return $total;
    }
}

class PercentageDiscount implements DiscountStrategy {
    public function __construct(private int $percent) {}
    
    public function calculate(float $total): float {
        return $total - ($total * $this->percent / 100);
    }
}

class FixedDiscount implements DiscountStrategy {
    public function __construct(private float $amount) {}
    
    public function calculate(float $total): float {
        return max(0, $total - $this->amount);
    }
}

تقبل class context أي استراتيجية، وتفوّض delegates عملية الحساب:

<?php
class ShoppingCart {
    public function __construct(private DiscountStrategy $discount) {}
    
    public function setDiscount(DiscountStrategy $discount): void {
        $this->discount = $discount;
    }
    
    public function checkout(float $total): float {
        return $this->discount->calculate($total);
    }
}

$cart = new ShoppingCart(new NoDiscount());
echo $cart->checkout(100) . "\n";

$cart->setDiscount(new PercentageDiscount(20));
echo $cart->checkout(100);

المخرجات:

100
80

يتألق نمط Strategy عندما تكون لديك طرق متعددة لتنفيذ عملية ما وتريد التبديل بينها دون تعديل الفئة التي تستخدمها. تعني إضافة نوع جديد من discount إنشاء فئة جديدة، وليس تعديل الشيفرة الحالية.

challenge icon

التحدي

سهل

لنَبْنِ حاسبةً لتكلفة الشحن باستخدام نمط الاستراتيجية (Strategy Pattern). تتميز طرق الشحن المختلفة بقواعد تسعير مختلفة: يفرض الشحن القياسي رسومًا ثابتة، ويضيف الشحن السريع نسبة مئوية، بينما يجمع الشحن طوال الليل بين الاثنين. يتيح لك نمط الاستراتيجية تبديل عمليات الحساب هذه دون تغيير الكود الذي يستخدمها.

ستنظّم الكود عبر أربعة ملفات:

  • ShippingStrategy.php: أنشئ واجهة ShippingStrategy التي تحدد العقد لجميع طرق الشحن. يجب أن تحتوي على method calculate(float $weight): float تأخذ وزن الطرد وتُرجع تكلفة الشحن.
  • ShippingMethods.php: ضمّن ملف ShippingStrategy وأنشئ ثلاث classes تطبّق الواجهة:
    • StandardShipping: تفرض سعرًا ثابتًا قدره $5.00 بغض النظر عن الوزن
    • ExpressShipping: تفرض $2.00 لكل kilogram (اضرب الوزن في 2)
    • OvernightShipping: تفرض fee أساسية قدرها $10.00 بالإضافة إلى $3.00 لكل kilogram
  • ShippingCalculator.php: ضمّن ملف ShippingStrategy وأنشئ class باسم ShippingCalculator يعمل باعتباره السياق. يجب أن تقوم الحاسبة بما يلي:
    • Accept كائنًا من نوع ShippingStrategy في constructor الخاص بها (استخدم constructor promotion)
    • Have method باسم setStrategy(ShippingStrategy $strategy): void لتغيير طريقة الشحن أثناء التشغيل
    • Have method باسم calculateCost(float $weight): float تفوّض العملية إلى الاستراتيجية الحالية
  • main.php: ضمّن ملفي ShippingMethods وShippingCalculator. ستتلقى مدخلين: نوع الشحن ("standard" أو "express" أو "overnight") ووزن الطرد.

    أنشئ ShippingCalculator بدءًا باستخدام StandardShipping. بناءً على مدخل نوع الشحن، استخدم setStrategy() للتبديل إلى طريقة الشحن المناسبة. ثم احسب التكلفة للوزن given واطبع النتيجة منسّقة إلى منزلتين عشريتين.

    يجب أن يكون الناتج: Shipping cost: $[cost]

يوضح هذا التحدي كيف يجعل نمط الاستراتيجية نظام الشحن مرنًا: فإضافة طريقة شحن جديدة مثل SameDayShipping لا تتطلب سوى إنشاء new class تطبّق الواجهة، من دون تعديل الحاسبة أو طرق الشحن الحالية.

جرّب بنفسك

<?php
require_once 'ShippingMethods.php';
require_once 'ShippingCalculator.php';

// اقرأ الإدخال
$shippingType = trim(fgets(STDIN));
$weight = floatval(fgets(STDIN));

// TODO: أنشئ ShippingCalculator بدءًا بـ StandardShipping

// TODO: بناءً على إدخال نوع الشحن ("standard" أو "express" أو "overnight")،
// استخدم setStrategy() للتبديل إلى طريقة الشحن المناسبة

// TODO: احسب التكلفة للوزن المعطى

// TODO: اطبع النتيجة منسّقة إلى منزلتين عشريتين
// Output format: Shipping cost: $[cost]

?>
quiz iconاختبر نفسك

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

جميع دروس البرمجة كائنية التوجه

تدرّب بنفسك: مترجم PHP عبر الإنترنت