Menu
Coddy logo textTech

תבנית המורכב

חלק מהיחידה תכנות מונחה עצמים במסלול ה-PHP של Coddy. שיעור 80 מתוך 91.

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

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

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

<?php
interface FileSystemItem {
    public function getName(): string;
    public function getSize(): int;
}

class File implements FileSystemItem {
    public function __construct(
        private string $name,
        private int $size
    ) {}
    
    public function getName(): string {
        return $this->name;
    }
    
    public function getSize(): int {
        return $this->size;
    }
}

class Folder implements FileSystemItem {
    private array $items = [];
    
    public function __construct(private string $name) {}
    
    public function getName(): string {
        return $this->name;
    }
    
    public function add(FileSystemItem $item): void {
        $this->items[] = $item;
    }
    
    public function getSize(): int {
        $total = 0;
        foreach ($this->items as $item) {
            $total += $item->getSize();
        }
        return $total;
    }
}

עכשיו אפשר לבנות מבני עץ ולהתייחס אליהם באופן אחיד:

<?php
$docs = new Folder("Documents");
$docs->add(new File("resume.pdf", 250));
$docs->add(new File("cover.docx", 50));

$images = new Folder("Images");
$images->add(new File("photo.jpg", 1500));

$root = new Folder("Root");
$root->add($docs);
$root->add($images);
$root->add(new File("readme.txt", 10));

echo $root->getSize();

פלט:

1810

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

challenge icon

אתגר

קל

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

תארגנו את הקוד שלכם בארבעה קבצים:

  • OrganizationUnit.php: הגדירו ממשק OrganizationUnit שגם עובדים יחידים וגם מחלקות יממשו. צריכות להיות לו שתי שיטות: getName(): string ו-getSalary(): int. הממשק המשותף הזה מאפשר להתייחס באותו אופן לעובדים יחידים ולמחלקות שלמות בעת חישוב העלויות.
  • Employee.php: כללו את הממשק OrganizationUnit וצרו מחלקת Employee שמממשת אותו. עובד הוא צומת עלה בעץ הארגוני שלכם. יש לו שם ושכר, אך הוא אינו יכול להכיל יחידות אחרות. השתמשו בקידום מאפיינים בבנאי כדי לקבל את השם והשכר, וממשו את שתי שיטות הממשק.
  • Department.php: כללו את הממשק OrganizationUnit וצרו מחלקת Department שמממשת אותו. מחלקה היא אובייקט מורכב שיכול להכיל עובדים וגם מחלקות אחרות. המחלקה שלכם צריכה:
    • לקבל שם בבנאי שלה
    • לממש את getName() כדי להחזיר את שם המחלקה
    • להכיל שיטה add(OrganizationUnit $unit): void להוספת עובדים או תת־מחלקות
    • לממש את getSalary() כדי להחזיר את השכר הכולל של כל היחידות שהיא מכילה (על ידי חיבור רקורסיבי של כל מה שנמצא בתוכה)
  • main.php: כללו את הקבצים Employee ו-Department. תקבלו קלט אחד: מחרוזת JSON שמייצגת את מבנה הארגון.

    ה-JSON יהיה בפורמט הזה:

    {"Engineering": [{"name": "Alice", "salary": 80000}, {"name": "Bob", "salary": 75000}], "Sales": [{"name": "Carol", "salary": 65000}]}

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

    צרו מחלקת שורש Department בשם "Company". עבור כל מחלקה ב-JSON, צרו אובייקט Department, הוסיפו אליו את העובדים שלה, ואז הוסיפו את המחלקה לחברה. לבסוף, הדפיסו את השכר הכולל של החברה בפורמט הזה:

    Total Salary: [amount]

היופי בתבנית Composite הוא שקריאה ל-getSalary() במחלקת החברה מחשבת אוטומטית את הסכום של כל המחלקות והעובדים המקוננים: קוד הלקוח אינו צריך להכיר את המבנה הפנימי.

נסו בעצמכם

<?php
require_once 'Employee.php';
require_once 'Department.php';

// קרא את קלט ה-JSON
$input = trim(fgets(STDIN));
$data = (array)json_decode($input, true);

// TODO: צור Department ראשית בשם "Company"

// TODO: עבור על כל מחלקה בנתוני ה-JSON
// עבור כל מחלקה:
//   - צור אובייקט Department חדש
//   - הוסף כל עובד למחלקה הזו
//   - הוסף את המחלקה ל-Company

// TODO: הדפס את השכר הכולל בפורמט: Total Salary: [amount]

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

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

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

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