תבנית Strategy
חלק מהיחידה תכנות מונחה עצמים במסלול ה-PHP של Coddy. שיעור 74 מתוך 91.
תבנית האסטרטגיה היא תבנית עיצוב התנהגותית שמאפשרת להגדיר משפחה של אלגוריתמים, לעטוף כל אחד מהם במחלקה משלו ולאפשר להחליף ביניהם בזמן ריצה. האובייקט שמשתמש באסטרטגיה אינו צריך לדעת איזה אלגוריתם הוא מריץ.
דמיינו עגלת קניות שיכולה להחיל אסטרטגיות הנחה שונות. במקום לקבע את לוגיקת ההנחה בקוד באמצעות תנאים, מגדירים כל הנחה כמחלקה נפרדת:
<?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);
}
}
מחלקת ההקשר מקבלת כל אסטרטגיה ומאצילה לה את החישוב:
<?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 Pattern). לשיטות משלוח שונות יש כללי תמחור שונים: משלוח רגיל גובה תעריף קבוע, משלוח אקספרס מוסיף אחוז, ומשלוח לילה משלב בין השניים. תבנית האסטרטגיה מאפשרת להחליף בין החישובים האלה בלי לשנות את הקוד שמשתמש בהם.
תארגנו את הקוד בארבעה קבצים:
ShippingStrategy.php: צרו ממשקShippingStrategyשמגדיר את החוזה עבור כל שיטות המשלוח. צריכה להיות בו מתודהcalculate(float $weight): floatשמקבלת את משקל החבילה ומחזירה את עלות המשלוח.ShippingMethods.php: כללו את קובץ ShippingStrategy וצרו שלוש מחלקות שמממשות את הממשק:StandardShipping: גובה תעריף קבוע של$5.00ללא קשר למשקלExpressShipping: גובה$2.00לכל קילוגרם (הכפילו את המשקל ב-2)OvernightShipping: גובה עמלת בסיס של$10.00ועוד$3.00לכל קילוגרם
ShippingCalculator.php: כללו את קובץ ShippingStrategy וצרו מחלקהShippingCalculatorשמשמשת כהקשר. המחשבון שלכם צריך:- לקבל
ShippingStrategyבבנאי שלו (השתמשו בקידום מאפיינים בבנאי) - להכיל מתודה
setStrategy(ShippingStrategy $strategy): voidלשינוי שיטת המשלוח בזמן הריצה - להכיל מתודה
calculateCost(float $weight): floatשמעבירה את החישוב לאסטרטגיה הנוכחית
- לקבל
main.php: כללו את הקבצים ShippingMethods ו-ShippingCalculator. תקבלו שתי כניסות: סוג משלוח ("standard","express"או"overnight") ומשקל חבילה.צרו
ShippingCalculatorשמתחיל עםStandardShipping. בהתאם לקלט של סוג המשלוח, השתמשו ב-setStrategy()כדי לעבור לשיטת המשלוח המתאימה. לאחר מכן חשבו את העלות עבור המשקל הנתון והדפיסו את התוצאה בפורמט של שתי ספרות אחרי הנקודה העשרונית.הפלט צריך להיות:
Shipping cost: $[cost]
האתגר הזה מדגים כיצד תבנית האסטרטגיה הופכת את מערכת המשלוחים שלכם לגמישה: הוספת שיטת משלוח חדשה, כמו SameDayShipping, דורשת רק יצירת מחלקה חדשה שמממשת את הממשק, בלי לגעת במחשבון או בשיטות המשלוח הקיימות.
נסו בעצמכם
<?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: הדפס את התוצאה בפורמט של שתי ספרות אחרי הנקודה העשרונית
// פורמט הפלט: Shipping cost: $[cost]
?>השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות התכנות מונחה העצמים (OOP)
קבצים חיצונייםמבוא ל-OOPמחלקות לעומת אובייקטיםמילת המפתח $thisמתודותמאפייניםבנאי (__construct)הורס (__destruct)חזרה: מחשבון פשוט4ירושה
ירושה בסיסיתמילת המפתח parent::דריסת מתודותמילת המפתח finalמחלקות מופשטותסיכום – היררכיית עובדים7כימוס
ציבורי, מוגן, פרטימפרטי גישה לעומקמתודות גישה ועדכוןהסתרת מידעקידום פרמטרים בבנאי (8.0)סיכום – מערכת רישומי תלמידים2מרחבי שמות וטעינה אוטומטית
מבוא למרחבי שמותמילת המפתח useתקן הטעינה האוטומטית PSR-4הטוען האוטומטי של Composerסיכום – פרויקט מאורגן5ממשקים וחוזים
מבוא לממשקיםמימוש ממשקיםמימוש של כמה ממשקיםממשק לעומת מחלקה מופשטתרמזי טיפוסים עם ממשקיםסיכום - מחשבון צורות8מתודות קסם
מבוא למתודות קסם__toString ו־__debugInfo__get, __set, __isset, __unset__call ו־__callStatic__clone ושיבוט אובייקטים__serialize ו־__unserializeסיכום – אוסף מותאם אישית3מאפייני מחלקה
מאפייני מופע לעומת מאפיינים סטטייםקבועים במחלקותמתודות ומאפיינים סטטייםמאפיינים פרטיים ומוגניםמאפיינים לקריאה בלבד (PHP 8.1)סיכום – מנהל חשבון בנק6פולימורפיזם
בחינה מחדש של דריסת מתודותפולימורפיזם באמצעות ממשקיםרמזי טיפוסים וטיפוסי איחודקישור סטטי מאוחרסיכום — מעבד תשלומיםתרגלו בעצמכם: קומפיילר PHP אונליין