Composite-Muster
Teil des Abschnitts Objektorientierte Programmierung der PHP-Journey von Coddy — Lektion 80 von 91.
Das Composite Pattern ist ein Strukturmuster, mit dem Sie Objekte zu Baumstrukturen zusammenfügen und diese Strukturen dann so behandeln können, als wären sie einzelne Objekte. Es eignet sich hervorragend zur Darstellung von Teil-Ganzes-Hierarchien, bei denen Sie Einzelobjekte und Gruppen von Objekten einheitlich behandeln möchten.
Betrachten Sie ein Dateisystem. Ein Ordner kann Dateien und andere Ordner enthalten.
Wenn Sie die Gesamtgröße berechnen, möchten Sie dieselbe Methode sowohl für Dateien als auch für Ordner aufrufen, und Ordner sollten ihren Inhalt rekursiv summieren. Das Composite Pattern macht dies nahtlos möglich, indem es sowohl Blättern (Dateien) als auch Komposita (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 können Sie Baumstrukturen aufbauen und diese 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:
1810Der Client-Code ruft getSize() auf, ohne sich darum zu kümmern, ob es sich um eine einzelne Datei oder eine tief verschachtelte Ordnerstruktur handelt. Das Muster verarbeitet die Rekursion intern, wodurch komplexe Hierarchien einfach zu handhaben sind.
Aufgabe
EinfachLassen Sie uns ein Organigramm-System mit dem Composite Pattern erstellen. Unternehmen haben eine hierarchische Struktur, in der Abteilungen Mitarbeiter und andere Unterabteilungen enthalten können — eine perfekte Baumstruktur, in der Sie Summen einheitlich über die gesamte Organisation hinweg berechnen möchten.
Sie werden Ihren Code über vier Dateien organisieren:
OrganizationUnit.php— Definieren Sie einOrganizationUnit-Interface, das sowohl einzelne Mitarbeiter als auch Abteilungen implementieren werden. Es sollte zwei Methoden haben:getName(): stringundgetSalary(): int. Dieses gemeinsame Interface ermöglicht es Ihnen, einzelne Mitarbeiter und ganze Abteilungen bei der Kostenberechnung gleich zu behandeln.Employee.php— Binden Sie das OrganizationUnit-Interface ein und erstellen Sie eineEmployee-Klasse, die es implementiert. Ein Mitarbeiter ist ein Blattknoten in Ihrem Organisationsbaum — er hat einen Namen und ein Gehalt, kann aber keine anderen Einheiten enthalten. Verwenden Sie Constructor Promotion, um den Namen und das Gehalt zu akzeptieren, und implementieren Sie beide Interface-Methoden.Department.php— Binden Sie das OrganizationUnit-Interface ein und erstellen Sie eineDepartment-Klasse, die es implementiert. Eine Abteilung ist ein Kompositum, das sowohl Mitarbeiter als auch andere Abteilungen enthalten kann. Ihre Abteilung sollte:- Einen Namen in ihrem Konstruktor akzeptieren
- Eine
add(OrganizationUnit $unit): void-Methode haben, um Mitarbeiter oder Unterabteilungen hinzuzufügen getSalary()implementieren, um das Gesamtgehalt aller darin enthaltenen Einheiten zurückzugeben (indem alles darin enthaltene rekursiv summiert wird)
main.php— Binden Sie die Dateien Employee und Department ein. Sie erhalten eine Eingabe: einen JSON-String, der die Organisationsstruktur darstellt.Das JSON wird in diesem Format vorliegen:
{"Engineering": [{"name": "Alice", "salary": 80000}, {"name": "Bob", "salary": 75000}], "Sales": [{"name": "Carol", "salary": 65000}]}Jeder Schlüssel ist ein Abteilungsname, und sein Wert ist ein Array von Mitarbeitern mit ihren Namen und Gehältern.
Erstellen Sie ein Wurzel-
Departmentnamens"Company". Erstellen Sie für jede Abteilung im JSON einDepartment, fügen Sie dessen Mitarbeiter hinzu und fügen Sie diese Abteilung dann der Company hinzu. Geben Sie abschließend das Gesamtgehalt des Unternehmens in diesem Format aus:Total Salary: [amount]
Das Schöne 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.
Spickzettel
Das Composite Pattern (Kompositum-Muster) ist ein Strukturmuster, das Objekte zu Baumstrukturen zusammenfügt und es ermöglicht, einzelne Objekte und Gruppen einheitlich zu behandeln.
Es ist ideal für Teil-Ganzes-Hierarchien, bei denen Sie dieselbe Schnittstelle sowohl für Blattknoten als auch für zusammengesetzte Knoten wünschen.
Kernkomponenten
- Component Interface — Definiert gemeinsame Operationen für sowohl Blätter als auch Komposita
- Leaf — Repräsentiert einzelne Objekte, die keine anderen Objekte enthalten können
- Composite — Enthält Kinder (Blätter oder andere Komposita) und implementiert Operationen durch Delegierung an die Kinder
Beispiel: Dateisystem
<?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;
}
}
Verwendung
<?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
Der Client ruft getSize() einheitlich für Dateien und Ordner auf. Das Kompositum verarbeitet die Rekursion intern, was die Arbeit mit komplexen Hierarchien vereinfacht.
Probier es selbst
<?php
require_once 'Employee.php';
require_once 'Department.php';
// JSON-Eingabe lesen
$input = trim(fgets(STDIN));
$data = (array)json_decode($input, true);
// TODO: Erstelle eine Wurzel-Abteilung namens "Company"
// TODO: Durchlaufe jede Abteilung in den JSON-Daten
// Für jede Abteilung:
// - Erstelle ein neues Department-Objekt
// - Füge jeden Mitarbeiter zu dieser Abteilung hinzu
// - Füge die Abteilung zur Company hinzu
// TODO: Gib das Gesamtgehalt im Format aus: Total Salary: [amount]
?>Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienEinführung in die OOPKlassen vs. ObjekteDas $this-SchlüsselwortMethodenEigenschaftenKonstruktor (__construct)Destruktor (__destruct)Rückblick - Einfacher Taschenrechner4Vererbung
Grundlagen der VererbungDas parent:: SchlüsselwortMethoden überschreibenDas final SchlüsselwortAbstrakte KlassenZusammenfassung – Mitarbeiter-Hierarchie7Kapselung
Public, Protected, PrivateZugriffsmodifikatoren im DetailGetter und SetterInformation HidingConstructor Promotion (8.0)Zusammenfassung – Studentenverwaltungssystem10Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungDependency InjectionAnonyme KlassenEnums (PHP 8.1)Fibers (PHP 8.1)Objekt-Klonen im DetailGeneratoren & Iteratoren13Entwurfsmuster Teil 2
Command-MusterAdapter-MusterDecorator-MusterTemplate-Method-MusterZustandsmusterComposite-MusterRepository-Muster2Namensräume & Autoloading
Einführung in NamensräumeDas use-SchlüsselwortPSR-4 Autoloading-StandardComposer AutoloaderZusammenfassung – Organisiertes Projekt5Interfaces & Verträge
Einführung in InterfacesInterfaces implementierenMehrfache Interface-ImplementierungInterface vs. Abstrakte KlasseType Hinting mit InterfacesZusammenfassung – Formen-Rechner8Magische Methoden
Einführung in magische Methoden__toString & __debugInfo__get, __set, __isset, __unset__call & __callStatic__clone & Objekt-Klonen__serialize & __unserializeZusammenfassung - Eigene Collection11Typsystem & Fehlerbehandlung
TypdeklarationenNullable-TypenUnion- & Intersection-TypenException-KlassenEigene Exception-HierarchieTry, Catch, FinallyZusammenfassung - Formular-Validator14Projekt: Bibliotheksverwaltung
ProjektübersichtBuch- und Benutzerklassen3Klasseneigenschaften
Instanz- vs. statische EigenschaftenKonstanten in KlassenStatische Methoden & EigenschaftenPrivate & geschützte EigenschaftenReadonly-Eigenschaften (PHP 8.1)Zusammenfassung – Bankkonto-Manager6Polymorphismus
Method Overriding erneut betrachtetPolymorphismus über InterfacesType Hinting & Union TypesLate Static BindingZusammenfassung – Payment Processor9Traits
Einführung in TraitsVerwendung mehrerer TraitsKonfliktlösung bei TraitsAbstrakte Methoden in TraitsTraits vs. Vererbung12Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFactory-MusterObserver-MusterStrategy-Muster15Abschlussherausforderungen
E-Learning-PlattformBanksystemSpielcharakter-EntwicklungFahrzeugvermietung