Menu
Coddy logo textTech

תבנית העיצובן

חלק מהיחידה תכנות מונחה עצמים במסלול ה-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

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

challenge icon

אתגר

קל

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

נארגן את הקוד בארבעה קבצים:

  • Pizza.php: נגדיר ממשק Pizza שמגדיר את החוזה לכל הפיצות והתוספות. יהיו בו שתי מתודות: getDescription(): string ו-getPrice(): float.
  • BasePizza.php: נכלול את ממשק Pizza וניצור מחלקה MargheritaPizza שמממשת אותו. זו תהיה הפיצה הבסיסית שלנו, עם תיאור "Margherita" ומחיר 8.00.
  • Toppings.php: נכלול את ממשק Pizza וניצור שלוש מחלקות דקורטור שמממשות אותו. כל דקורטור עוטף אובייקט Pizza (יש לקבל אותו בבנאי) ומשדרג גם את התיאור וגם את המחיר:
    • CheeseTopping: מוסיפה לתיאור ", Extra Cheese" ולמחיר 1.50
    • PepperoniTopping: מוסיפה לתיאור ", Pepperoni" ולמחיר 2.00
    • MushroomTopping: מוסיפה לתיאור ", 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()

?>
quiz iconבחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה תכנות מונחה עצמים

תרגלו בעצמכם: קומפיילר PHP אונליין