תבנית העיצובן
חלק מהיחידה תכנות מונחה עצמים במסלול ה-PHP של Coddy. שיעור 77 מתוך 91.
תבנית העיצוב Decorator היא תבנית עיצוב מבנית שמאפשרת לך להוסיף באופן דינמי התנהגויות חדשות לאובייקטים באמצעות עטיפתם באובייקטי מעטפת מיוחדים שנקראים מעטרים. בניגוד לירושה, שמוסיפה התנהגות בזמן הקומפילציה, מעטרים מוסיפים התנהגות בזמן ריצה.
דמיינו בית קפה. מתחילים עם קפה בסיסי, ואז מוסיפים תוספות כמו חלב, סוכר או קצפת.
כל תוספת עוטפת את הקפה המקורי ומוסיפה לתיאור שלו ולעלות שלו. התובנה המרכזית היא שגם קפה הבסיס וגם העוטפים חולקים את אותו ממשק.
<?php
interface Coffee {
public function getDescription(): string;
public function getCost(): float;
}
class SimpleCoffee implements Coffee {
public function getDescription(): string {
return "Simple Coffee";
}
public function getCost(): float {
return 2.00;
}
}
דקורטורים מממשים את אותו ממשק ועוטפים אובייקט Coffee אחר:
<?php
class MilkDecorator implements Coffee {
public function __construct(private Coffee $coffee) {}
public function getDescription(): string {
return $this->coffee->getDescription() . ", Milk";
}
public function getCost(): float {
return $this->coffee->getCost() + 0.50;
}
}
class SugarDecorator implements Coffee {
public function __construct(private Coffee $coffee) {}
public function getDescription(): string {
return $this->coffee->getDescription() . ", Sugar";
}
public function getCost(): float {
return $this->coffee->getCost() + 0.25;
}
}
$coffee = new SimpleCoffee();
$coffee = new MilkDecorator($coffee);
$coffee = new SugarDecorator($coffee);
echo $coffee->getDescription() . " = $" . $coffee->getCost();
פלט:
Simple Coffee, Milk, Sugar = $2.75כל מקשט עוטף את האובייקט הקודם ויוצר שרשרת. התבנית מאפשרת לך לשלב התנהגויות בכל סדר ובכל כמות, בלי ליצור מחלקה לכל שילוב אפשרי. רוצה כמות כפולה של חלב? פשוט עטוף אותו פעמיים.
אתגר
קלנבנה מערכת להזמנת פיצה באמצעות תבנית העיצוב Decorator. נתחיל מפיצה בסיסית ונוסיף תוספות באופן דינמי: כל תוספת עוטפת את הפיצה ומוסיפה לתיאור ולמחיר שלה, ממש כמו בדוגמת הקפה מהשיעור.
נארגן את הקוד בארבעה קבצים:
Pizza.php: נגדיר ממשקPizzaשמגדיר את החוזה לכל הפיצות והתוספות. יהיו בו שתי מתודות:getDescription(): stringו-getPrice(): float.BasePizza.php: נכלול את ממשק Pizza וניצור מחלקהMargheritaPizzaשמממשת אותו. זו תהיה הפיצה הבסיסית שלנו, עם תיאור"Margherita"ומחיר8.00.Toppings.php: נכלול את ממשק Pizza וניצור שלוש מחלקות דקורטור שמממשות אותו. כל דקורטור עוטף אובייקטPizza(יש לקבל אותו בבנאי) ומשדרג גם את התיאור וגם את המחיר:CheeseTopping: מוסיפה לתיאור", Extra Cheese"ולמחיר1.50PepperoniTopping: מוסיפה לתיאור", Pepperoni"ולמחיר2.00MushroomTopping: מוסיפה לתיאור", Mushrooms"ולמחיר1.25
main.php: נכלול את הקבצים BasePizza ו-Toppings. תתקבל קלט אחד: רשימה מופרדת בפסיקים של תוספות להוספה (למשל,"cheese,pepperoni"או"mushroom,cheese,cheese").נתחיל עם
MargheritaPizza. עבור כל תוספת בקלט, נעטוף את הפיצה בדקורטור המתאים:"cheese"עוטף אותה באמצעותCheeseTopping"pepperoni"עוטף אותה באמצעותPepperoniTopping"mushroom"עוטף אותה באמצעותMushroomTopping
לאחר החלת כל התוספות, נדפיס את תיאור הפיצה הסופית ואת המחיר שלה בפורמט הבא:
[description] = $[price]יש לעצב את המחיר כך שיוצגו שתי ספרות אחרי הנקודה העשרונית.
שימו לב שאפשר להוסיף את אותה תוספת כמה פעמים: הזמנה של כמות כפולה של גבינה פירושה פשוט לעטוף באמצעות CheeseTopping פעמיים. זו הגמישות שתבנית העיצוב Decorator מעניקה!
נסו בעצמכם
<?php
require_once 'BasePizza.php';
require_once 'Toppings.php';
// קראו את הקלט - רשימת תוספות מופרדת בפסיקים
$input = trim(fgets(STDIN));
$toppings = explode(',', $input);
// התחילו עם MargheritaPizza בסיסית
$pizza = new MargheritaPizza();
// TODO: עברו על כל תוספת ועטפו את הפיצה בדקורטור המתאים
// - "cheese" -> עטפו באמצעות CheeseTopping
// - "pepperoni" -> עטפו באמצעות PepperoniTopping
// - "mushroom" -> עטפו באמצעות MushroomTopping
// TODO: הדפיסו את התוצאה בפורמט: [description] = $[price]
// עצבו את המחיר עם 2 ספרות אחרי הנקודה העשרונית באמצעות number_format()
?>השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות התכנות מונחה העצמים (OOP)
קבצים חיצונייםמבוא ל-OOPמחלקות לעומת אובייקטיםמילת המפתח $thisמתודותמאפייניםבנאי (__construct)הורס (__destruct)חזרה: מחשבון פשוט4ירושה
ירושה בסיסיתמילת המפתח parent::דריסת מתודותמילת המפתח finalמחלקות מופשטותסיכום – היררכיית עובדים7כימוס
ציבורי, מוגן, פרטימפרטי גישה לעומקמתודות גישה ועדכוןהסתרת מידעקידום פרמטרים בבנאי (8.0)סיכום – מערכת רישומי תלמידים10מושגי OOP מתקדמים
הרכבה לעומת ירושההזרקת תלויותמחלקות אנונימיותטיפוסי Enum (PHP 8.1)סיבים (PHP 8.1)העמקה בשכפול אובייקטיםמחוללים ואיטרטורים13תבניות עיצוב חלק 2
תבנית הפקודהתבנית המתאםתבנית העיצובןתבנית שיטת התבניתתבנית המצבתבנית המורכבתבנית המאגר2מרחבי שמות וטעינה אוטומטית
מבוא למרחבי שמותמילת המפתח useתקן הטעינה האוטומטית PSR-4הטוען האוטומטי של Composerסיכום – פרויקט מאורגן5ממשקים וחוזים
מבוא לממשקיםמימוש ממשקיםמימוש של כמה ממשקיםממשק לעומת מחלקה מופשטתרמזי טיפוסים עם ממשקיםסיכום - מחשבון צורות8מתודות קסם
מבוא למתודות קסם__toString ו־__debugInfo__get, __set, __isset, __unset__call ו־__callStatic__clone ושיבוט אובייקטים__serialize ו־__unserializeסיכום – אוסף מותאם אישית3מאפייני מחלקה
מאפייני מופע לעומת מאפיינים סטטייםקבועים במחלקותמתודות ומאפיינים סטטייםמאפיינים פרטיים ומוגניםמאפיינים לקריאה בלבד (PHP 8.1)סיכום – מנהל חשבון בנק6פולימורפיזם
בחינה מחדש של דריסת מתודותפולימורפיזם באמצעות ממשקיםרמזי טיפוסים וטיפוסי איחודקישור סטטי מאוחרסיכום — מעבד תשלומיםתרגלו בעצמכם: קומפיילר PHP אונליין