Menu
Coddy logo textTech

Kompositummuster

Teil des Abschnitts Objektorientierte Programmierung der PHP-Journey von Coddy. Lektion 80 von 91.

Das Composite Pattern ist ein strukturelles Entwurfsmuster, mit dem du Objekte zu Baumstrukturen zusammensetzen und anschließend mit diesen Strukturen so arbeiten kannst, als wären sie einzelne Objekte. Es eignet sich perfekt zur Darstellung von Teil-Ganzes-Hierarchien, bei denen du einzelne Objekte und Objektgruppen einheitlich behandeln möchtest.

Betrachte ein Dateisystem. Ein Ordner kann Dateien und andere Ordner enthalten.

Wenn du die Gesamtgröße berechnest, möchtest du dieselbe Methode sowohl für Dateien als auch für Ordner aufrufen, wobei Ordner den Inhalt rekursiv summieren sollen. Das Kompositum-Muster macht dies nahtlos möglich, indem es sowohl Blättern (Dateien) als auch Kompositen (Ordnern) dieselbe Schnittstelle gibt.

<?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;
    }
}

Jetzt kannst du Baumstrukturen erstellen und einheitlich behandeln:

<?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();

Ausgabe:

1810

Der Client-Code ruft getSize() auf, ohne sich darum zu kümmern, ob er mit einer einzelnen Datei oder einer tief verschachtelten Ordnerstruktur arbeitet. Das Muster verarbeitet die Rekursion intern und macht den Umgang mit komplexen Hierarchien einfach.

challenge icon

Aufgabe

Einfach

Erstellen wir ein Organigrammsystem mithilfe des Composite Patterns. Unternehmen haben eine hierarchische Struktur, in der Abteilungen Mitarbeiter und andere Unterabteilungen enthalten können: eine perfekte Baumstruktur, bei der du Summen einheitlich über die gesamte Organisation hinweg berechnen möchtest.

Du wirst deinen Code auf vier Dateien verteilen:

  • OrganizationUnit.php: Definiere ein OrganizationUnit-Interface, das sowohl einzelne Mitarbeiter als auch Abteilungen implementieren. Es sollte zwei Methoden enthalten: getName(): string und getSalary(): int. Dieses gemeinsame Interface ermöglicht es dir, einzelne Mitarbeiter und ganze Abteilungen bei der Kostenberechnung auf dieselbe Weise zu behandeln.
  • Employee.php: Binde das OrganizationUnit-Interface ein und erstelle eine Employee-Klasse, die es implementiert. Ein Mitarbeiter ist ein Blattknoten in deinem Organisationsbaum. Er hat einen Namen und ein Gehalt, kann aber keine anderen Einheiten enthalten. Verwende Konstruktor-Promotion, um den Namen und das Gehalt entgegenzunehmen, und implementiere beide Interface-Methoden.
  • Department.php: Binde das OrganizationUnit-Interface ein und erstelle eine Department-Klasse, die es implementiert. Eine Abteilung ist ein Kompositum, das sowohl Mitarbeiter als auch andere Abteilungen enthalten kann. Deine Abteilung sollte:
    • einen Namen in ihrem Konstruktor entgegennehmen
    • getName() implementieren, um den Namen der Abteilung zurückzugeben
    • über eine Methode add(OrganizationUnit $unit): void verfügen, um Mitarbeiter oder Unterabteilungen hinzuzufügen
    • getSalary() implementieren, um das Gesamtgehalt aller enthaltenen Einheiten zurückzugeben (indem rekursiv alles innerhalb der Abteilung summiert wird)
  • main.php: Binde die Dateien Employee und Department ein. Du erhältst eine Eingabe: einen JSON-String, der die Organisationsstruktur darstellt.

    Das JSON hat dieses Format:

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

    Jeder Schlüssel ist der Name einer Abteilung, und sein Wert ist ein Array von Mitarbeitern mit ihren Namen und Gehältern.

    Erstelle eine Stamm-Department namens "Company". Erstelle für jede Abteilung im JSON eine Department-Instanz, füge ihre Mitarbeiter hinzu und füge diese Abteilung anschließend zu Company hinzu. Gib schließlich das Gesamtgehalt des Unternehmens in diesem Format aus:

    Total Salary: [amount]

Das Besondere am Composite Pattern ist, dass der Aufruf von getSalary() für die Company-Abteilung automatisch die Summe über alle verschachtelten Abteilungen und Mitarbeiter berechnet: Der Client-Code muss die interne Struktur nicht kennen.

Probier es selbst

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

// Lies die JSON-Eingabe
$input = trim(fgets(STDIN));
$data = (array)json_decode($input, true);

// TODO: Erstelle ein Root-Department namens "Company"

// TODO: Durchlaufe jedes Department in den JSON-Daten
// Für jedes Department:
//   - Erstelle ein neues Department-Objekt
//   - Füge jeden Mitarbeiter zu diesem Department hinzu
//   - Füge das Department zur Company hinzu

// TODO: Gib das Gesamtgehalt im Format aus: Total Salary: [amount]

?>
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Online-PHP-Compiler