תחי ישראל - אין לנו ארץ אחרת

תחי ישראל -אין לנו ארץ אחרת

דפוס תכנותי אסטרטגיה ב-PHP

מחבר:
בתאריך:

ראינו במדריכים קודמים כיצד דפוסים תכנותיים פותרים בעיות פרקטיות באמצעות שימוש במבנה הקוד (MVC, מדריך factory design pattern ב-PHP). דפוס אסטרטגיה הוא דפוס תכנותי שמשתמשים בו כשצריך לבחור בין חלופות קוד לטיפול במקרים דומים שיש להם יישום שונה. במקרים אילו, מציע הדפוס ליצור interface שאותו יישמו המחלקות השונות. כשהבחירה מאיזו מחלקה ליצור אובייקט נעשית בזמן הריצה של התכנית.

 

מאפייני דפוס האסטרטגיה

אנחנו יכולים לתת סימנים בדפוס האסטרטגיה. הראשון הוא השימוש ב-interface והשני הוא השימוש בבורר. הבורר הוא קוד שבוחר מאיזה מהמחלקות שמיישמות את ה-interface ליצור את האובייקט.

 

מתי לבחור בדפוס האסטרטגיה?

משתמשים בדפוס האסטרטגיה כאשר רוצים לטפל בחלופות קוד דומות שיש להם יישום שונה.

מה זה חלופות קוד? בדוגמה הבאה, הפונקציה cupounGenerator מייצרת קופונים לפי סוג המכונית. למעוניינים ברכב מסוג Bmw מוצע קופון אחר מלמעוניינים במרצדס.

function cupounGenerator($car)
{
  if($car == "bmw")
  {
    $cupoun = "Get 5% off the price of your new BMW";
  }
  else if($car == "mercedes")
  {
    $cupoun = "Get 7% off the price of your new Mercedes";
  }
  
  return $cupoun;
}

הקופון הוא שיעור ההנחה המוצע ללקוח שמעוניין לרכוש את המכונית, כשניתן להביא בחשבון מספר גורמים לצורך שקלול שיעור ההנחה. לדוגמה, ניתן לשקלל לתוך ההחלטה על שיעור ההנחה את הגורמים הבאים:

  • התקופה בשנה - ייתכן שנרצה להציע הנחה בתקופות של שפל ברכישת מכוניות
  • מלאי הרכבים - ייתכן שנרצה להציע הנחה כאשר מלאי הרכבים למכירה גדול מדי

נשכתב את הפונקציה cupounGenerator כדי שתתחשב בגורמים אילו. לשם כך, נוסיף את המשתנים הבאים:

  • isHighSeason$- שמציין תקופה של שיא במכירות
  • bigStock$ - שמציין מספר גדול של מכוניות במלאי

כך נראה הקוד המשוכתב שעכשיו כולל את המשתנים החדשים שמשפיעים על שיעור ההנחה:

function cupounGenerator($car)
{
  $discount = 0;
  
  $isHighSeason = false;
  
  $bigStock = true;
  
  
  if($car == "bmw")
  {
    if(!$isHighSeason) {$discount += 5;}
  
    if($bigStock) {$discount += 7;}
  }
  else if($car == "mercedes")
  {
    if(!$isHighSeason) {$discount += 4;}
  
    if($bigStock) {$discount += 10;}
  }
  
  return $cupoun = "Get {$discount}% off the price of your new car.";
}
  
  
echo cupounGenerator("bmw");

והתוצאה:

Get 12% off the price of your new car.

 

מקוד פרוצדורלי לקוד מבוסס אובייקטים

עד עכשיו, הקוד היה די פשוט ולא חייב דבר מעבר לקוד פרוצדורלי שנסמך על פונקציה אחת שנותנת הנחה לפי יצרן. במציאות, מערכת השיקולים יכולה להיות מורכבת הרבה יותר, מה שיחייב אותנו לכתוב מספר פונקציות שבהם אנו יכולים להשתמש כדי לקבוע את שיעור ההנחה.

לפי מערכת השיקולים שראינו, אנו צריכים ליצור את שתי הפונקציות הבאות:

פונקציה שמוסיפה הנחה כשהמכירות בתקופת שפל. נקרא לה:

addSeasonDiscount

פונקציה שמוסיפה הנחה כשהמלאי גדול מדי, ושמה בהתאם:

addStockDiscount

במקרים שבהם נרצה לטפל באופן שונה בסוגים השונים של המכוניות, כדאי לנו לכנס את הפונקציות תחת מחלקות נפרדות. מחלקה לכל סוג של מכונית. במקרה שלנו, נכתוב מחלקה אחת לייצור קופונים עבור BMW ואחרת שתייצר קופונים עבור מרצדס. המחלקה שתייצר קופונים עבור BMW תקרא:

bmwCupounGenerator

והמחלקה שתייצר קופונים עבור מרצדס תקרא:

mercedesCupounGenerator

 

ה-interface

למעשה, אנו מעוניינים לכתוב שתי מחלקות שיש להם מתודות באותו שם. במצב כזה, כדאי לנו ליצור interface שיחייב את המחלקות, גם את המחלקות שנכתוב מיד וגם את אילו שנכתוב בעתיד.

נקרא ל-interface בשם carCupounGenerator, והוא יכלול את המתודות הרצויות לנו:

// interface to commit the classes that generate coupons.
interface carCouponGenerator {
  function addSeasonDiscount();
  function addStockDiscount();
}

 

המחלקות שמיישמות את ה-interface

אחרי שכתבנו את ה-interface, אנחנו יכולים לכתוב את המחלקות שיישמו את ה-interface.

נתחיל עם ה-bmwCupounGenerator שמייצרת קופונים לרכבי BMW:

// implements the interface for BMW.
class bmwCouponGenerator implements carCouponGenerator {
  private $discount = 0;
  private $isHighSeason = false;
  private $bigStock = true;
  
  public function addSeasonDiscount()
  {
    if(!$this->isHighSeason) return $this->discount += 5;
    return $this->discount +=0;
  }
  
  public function addStockDiscount()
  {
    if($this->bigStock) return $this->discount += 7;
    return $this->discount +=0;
  }
}

וגם את המחלקה mercedesCupounGenerator שמייצרת קופונים לרכבי מרצדס:

// implements the interface for Mercedes.
class mercedesCouponGenerator implements carCouponGenerator {
  private $discount = 0;
  private $isHighSeason = false;
  private $bigStock = true;
  
  public function addSeasonDiscount()
  {
    if(!$this->isHighSeason) return $this->discount += 4;
    return $this->discount +=0;
  }
  
  public function addStockDiscount()
  {
    if($this->bigStock) return $this->discount += 10;
    return $this->discount +=0;
  }
}

עד כה, ראינו את אחד משני המאפיינים החשובים ביותר של דפוס האסטרטגיה שהוא השימוש ב-interface כדי להכתיב למחלקות שמיישמות אותו איזה קוד הן צריכות לכלול.

 

הבורר

המאפיין השני של דפוס האסטרטגיה הוא השימוש בבורר. הבורר הוא אותו חלק של הקוד שמחליט מאילו מחלקות ליצור את האובייקט, ומייצר את האובייקט בהתאם.

את תפקיד הבורר תמלא בדוגמה הפונקציה cupounObjectGenerator שבוחרת איזה אובייקט ליצור (BMW או מרצדס), ואז מייצרת אותו. לפונקציה זו אנו מזינים את סוג המכונית המבוקש באמצעות המשתנה car$, ובהתאם היא בוחרת האם לייצר אובייקט מ- bmwCupounGenerator או מ- mercedesCupounGenerator.

// Create object from the alternative classes.
function couponObjectGenerator($car)
{
  if($car == "bmw")
  {
    $carObj = new bmwCouponGenerator;
  }
  else if($car == "mercedes")
  {
    $carObj = new mercedesCouponGenerator;
  }
  
  return $carObj;
}

הבורר הוא מאוד חשוב בדפוס האסטרטגיה מפני שהוא מאפשר בזמן הריצה של התכנית לבחור איזה אובייקט ליצור, ואז ליצור אותו.

 

יישום

ראינו כיצד הבורר בוחר מאיזה מחלקה של ה-interface ליצור אובייקט, ובאובייקט זה אנחנו יכולים להשתמש עכשיו כדי לנפק את הקופון. לשם כך, נכתוב את המחלקה couponGenerator שמקבלת את האובייקט שיצרנו באמצעות הבורר, ומחזירה קופון באמצעות המתודה getCupoun. המתודה getCupoun משתמשת למעשה במתודות שהגיעו מהמחלקות שמיישמות את ה-interface.

// Generate coupon from the object of choice.
class couponGenerator {
  private $carCoupon;
  
  public function __construct(carCouponGenerator $carCoupon)
  {
    $this->carCoupon = $carCoupon;
  }
  
  public function getCoupon()
  {
    $discount = $this->carCoupon->addSeasonDiscount();
    $discount = $this->carCoupon->addStockDiscount();
  
    return $coupon = "Get {$discount}% off the price of your new car.";
  }
}

נשתמש בקוד הבא כדי לבדוק את התוצאה. בקוד אנחנו יכולים לראות שני שלבים:

1. יוצרים אובייקט מהמחלקות שיורשות מה-interface. שימו לב, couponObjectGenerator גם יוצר את האובייקט וגם בורר מאיזה מחלקה ליצור את האובייקט.

2. מזינים את האובייקט שיצרנו בצעד הראשון ל-couponGenerator שמייצר קופונים באמצעות המתודה getCoupon.

// Test the code.
$car = "bmw";
$carObj = couponObjectGenerator($car);
$couponGenerator = new couponGenerator($carObj);
echo $couponGenerator->getCoupon();
  
echo "<hr />";
  
$car = "mercedes";
$carObj = couponObjectGenerator($car);
$couponGenerator = new couponGenerator($carObj);
echo $couponGenerator->getCoupon();

זה הקוד המלא שאותו כתבנו במדריך:

<?php
// interface to commit the classes that generate coupons.
interface carCouponGenerator {
  function addSeasonDiscount();
  function addStockDiscount();
}
  
// implements the interface for BMW.
class bmwCouponGenerator implements carCouponGenerator {
  private $discount = 0;
  private $isHighSeason = false;
  private $bigStock = true;
  
  public function addSeasonDiscount()
  {
    if(!$this->isHighSeason) return $this->discount += 5;
    return $this->discount +=0;
  }
  
  public function addStockDiscount()
  {
    if($this->bigStock) return $this->discount += 7;
    return $this->discount +=0;
  }
}
  
// implements the interface for Mercedes.
class mercedesCouponGenerator implements carCouponGenerator {
  private $discount = 0;
  private $isHighSeason = false;
  private $bigStock = true;
  
  public function addSeasonDiscount()
  {
    if(!$this->isHighSeason) return $this->discount += 4;
    return $this->discount +=0;
  }
  
  public function addStockDiscount()
  {
    if($this->bigStock) return $this->discount += 10;
    return $this->discount +=0;
  }
}
  
// Generate coupon from the object of choice.
class couponGenerator {
  private $carCoupon;
  
  public function __construct(carCouponGenerator $carCoupon)
  {
    $this->carCoupon = $carCoupon;
  }
  
  public function getCoupon()
  {
  $discount = $this->carCoupon->addSeasonDiscount();
  $discount = $this->carCoupon->addStockDiscount();
  
  return $coupon = "Get {$discount}% off the price of your new car.";
  }
}
 
// Create object from the alternative classes.
function couponObjectGenerator($car)
{
  if($car == "bmw")
  {
    $carObj = new bmwCouponGenerator;
  }
  else if($car == "mercedes")
  {
    $carObj = new mercedesCouponGenerator;
  }
 
  return $carObj;
}
 
 
// Test the code. $car = "bmw";
$carObj = couponObjectGenerator($car);
$couponGenerator = new couponGenerator($carObj);
echo $couponGenerator->getCoupon();
 
echo "<hr />";  
$car = "mercedes";
$carObj = couponObjectGenerator($car);
$couponGenerator = new couponGenerator($carObj);
echo $couponGenerator->getCoupon();

והתוצאה:

Get 12% off the price of your new car.
Get 14% off the price of your new car.

 

לסיכום

בדפוס תכנותי אסטרטגיה נשתמש באותם מקרים שבהם אנו מעוניינים ליצור אובייקט 1 מבין כמה חלופות דומות בזמן הריצה של התכנית. החלופות דומות בזה שהם יכולות להשתמש במתודות בעלות שם זהה, אבל הם נבדלות ביישום. לכן, דפוס האסטרטגיה משתמש ב-interface. הבחירה בין החלופות נעשית באמצעות קוד שבורר, ומחליט מאיזה מהמחלקות שמיישמות את ה-interface ליצור את האובייקט.

 

אני ממליץ ללמוד PHP מונחה עצמים עם "The essentials of Object Oriented PHP" שהוא הספר שעליו מבוססים רוב המדריכים בנושא באתר רשתטק.
הקליקו על התמונה כדי לרכוש את ה-eBook:

eBook cover The essentials of Object Oriented PHP

 

לכל מדריכי ה-PHP מונחה עצמים

 

אהבתם? לא אהבתם? דרגו!

0 הצבעות, ממוצע 0 מתוך 5 כוכבים

 

 

המדריכים באתר עוסקים בנושאי תכנות ופיתוח אישי. הקוד שמוצג משמש להדגמה ולצרכי לימוד. התוכן והקוד המוצגים באתר נבדקו בקפידה ונמצאו תקינים. אבל ייתכן ששימוש במערכות שונות, דוגמת דפדפן או מערכת הפעלה שונה ולאור השינויים הטכנולוגיים התכופים בעולם שבו אנו חיים יגרום לתוצאות שונות מהמצופה. בכל מקרה, אין בעל האתר נושא באחריות לכל שיבוש או שימוש לא אחראי בתכנים הלימודיים באתר.

למרות האמור לעיל, ומתוך רצון טוב, אם נתקלת בקשיים ביישום הקוד באתר מפאת מה שנראה לך כשגיאה או כחוסר עקביות נא להשאיר תגובה עם פירוט הבעיה באזור התגובות בתחתית המדריכים. זה יכול לעזור למשתמשים אחרים שנתקלו באותה בעיה ואם אני רואה שהבעיה עקרונית אני עשוי לערוך התאמה במדריך או להסיר אותו כדי להימנע מהטעיית הציבור.

שימו לב! הסקריפטים במדריכים מיועדים למטרות לימוד בלבד. כשאתם עובדים על הפרויקטים שלכם אתם צריכים להשתמש בספריות וסביבות פיתוח מוכחות, מהירות ובטוחות.

המשתמש באתר צריך להיות מודע לכך שאם וכאשר הוא מפתח קוד בשביל פרויקט הוא חייב לשים לב ולהשתמש בסביבת הפיתוח המתאימה ביותר, הבטוחה ביותר, היעילה ביותר וכמובן שהוא צריך לבדוק את הקוד בהיבטים של יעילות ואבטחה. מי אמר שלהיות מפתח זו עבודה קלה ?

השימוש שלך באתר מהווה ראייה להסכמתך עם הכללים והתקנות שנוסחו בהסכם תנאי השימוש.

הוסף תגובה חדשה

 

 

ענה על השאלה הפשוטה הבאה כתנאי להוספת תגובה:

דג למים הוא כמו ציפור ל...?

 

תמונת המגיב

כרמית בתאריך: 14.04.2023

אלוףףף מליון תודות עוזר מאוד