Composite-Muster
Teil des Abschnitts Objektorientierte Programmierung der Java-Journey von Coddy — Lektion 75 von 87.
Das Composite Pattern ist ein Strukturmuster, mit dem Sie Objekte zu Baumstrukturen zusammenfügen können, um Teil-Ganzes-Hierarchien darzustellen. Es ermöglicht es Clients, sowohl einzelne Objekte als auch Kompositionen von Objekten über eine gemeinsame Schnittstelle einheitlich zu behandeln.
Stellen Sie sich ein Dateisystem vor: Ein Ordner kann Dateien und andere Ordner enthalten, die wiederum weitere Dateien und Ordner enthalten können. Egal, ob Sie es mit einer einzelnen Datei oder einer gesamten Ordnerstruktur zu tun haben, Sie möchten Operationen wie „Größe abrufen“ auf die gleiche Weise ausführen. Das Muster definiert eine Component-Schnittstelle, Leaf-Objekte (einzelne Elemente) und Composite-Objekte (Container, die untergeordnete Elemente enthalten):
interface FileComponent {
void display(String indent);
int getSize();
}
class File implements FileComponent {
private String name;
private int size;
public File(String name, int size) {
this.name = name;
this.size = size;
}
public void display(String indent) {
System.out.println(indent + name + " (" + size + "KB)");
}
public int getSize() {
return size;
}
}
class Folder implements FileComponent {
private String name;
private List<FileComponent> children = new ArrayList<>();
public Folder(String name) {
this.name = name;
}
public void add(FileComponent component) {
children.add(component);
}
public void display(String indent) {
System.out.println(indent + name + "/");
for (FileComponent child : children) {
child.display(indent + " ");
}
}
public int getSize() {
return children.stream().mapToInt(FileComponent::getSize).sum();
}
}Das Kompositum delegiert Operationen an seine Kinder, was rekursive Strukturen ermöglicht. Clients interagieren mit dem Baum, ohne zu wissen, ob sie mit einem Blatt oder einem Kompositum arbeiten:
Folder root = new Folder("Documents");
root.add(new File("resume.pdf", 150));
Folder photos = new Folder("Photos");
photos.add(new File("vacation.jpg", 2000));
root.add(photos);
root.display("");
System.out.println("Total: " + root.getSize() + "KB");Das Composite Pattern ist ideal für die Darstellung von Hierarchien wie Organigrammen, UI-Komponenten oder Menüsystemen, bei denen Sie Gruppen und Einzelobjekte identisch behandeln müssen.
Aufgabe
EinfachLassen Sie uns ein Organigramm-System mit dem Composite Pattern erstellen! Sie werden eine Struktur aufbauen, in der Abteilungen Mitarbeiter und andere Unterabteilungen enthalten können. Dies ermöglicht es Ihnen, Gesamtgehälter zu berechnen und die Hierarchie einheitlich anzuzeigen – egal, ob Sie einen einzelnen Mitarbeiter oder eine gesamte Abteilung betrachten.
Sie werden Ihren Code über vier Dateien organisieren:
OrganizationComponent.java: Definieren Sie das Komponenten-Interface, das sowohl Mitarbeiter als auch Abteilungen implementieren werden. Es sollte zwei Methoden deklarieren:showDetails(String indent)zur Anzeige der Komponente mit korrekter Einrückung undgetSalary(), das das Gesamtgehalt als Ganzzahl zurückgibt.Employee.java: Erstellen Sie die Leaf-Klasse (Blatt-Klasse), die einzelne Mitarbeiter repräsentiert. EinEmployeehat einen Namen (String) und ein Gehalt (int), die beide über den Konstruktor gesetzt werden. WennshowDetailsaufgerufen wird, sollte es[indent][name]: $[salary]ausgeben. Die MethodegetSalarygibt einfach das Gehalt des Mitarbeiters zurück.Department.java: Erstellen Sie die Composite-Klasse (Zusammengesetzte Klasse), die sowohl Mitarbeiter als auch Unterabteilungen enthalten kann. EineDepartmenthat einen Namen (String) und verwaltet eine Liste vonOrganizationComponent-Kindern. Sie sollte eine Methodeadd(OrganizationComponent component)haben, um Mitglieder hinzuzufügen. WennshowDetailsaufgerufen wird, gibt sie[indent][name] Departmentaus und ruft dannshowDetailsfür jedes Kind mit erhöhter Einrückung (fügen Sie zwei Leerzeichen hinzu) auf. Die MethodegetSalarygibt die Summe der Gehälter aller Kinder zurück.Main.java: Bauen Sie Ihre Organisation auf! Sie erhalten vier Eingaben: zwei Mitarbeiternamen mit ihren Gehältern und einen Namen für eine Unterabteilung mit einem Mitarbeiter.Erstellen Sie eine "Engineering"-Abteilung als Ihre Wurzel. Fügen Sie ihr zwei Mitarbeiter mit den ersten beiden Name/Gehalt-Paaren hinzu. Erstellen Sie dann eine Unterabteilung mit der dritten Eingabe, fügen Sie ihr einen Mitarbeiter mit dem vierten Name/Gehalt-Paar hinzu und fügen Sie diese Unterabteilung der Engineering-Abteilung hinzu.
Rufen Sie
showDetails("")für die Engineering-Abteilung auf und geben Sie dann das Gesamtgehalt im FormatTotal Salary: $[amount]aus.
Sie erhalten die Eingaben in dieser Reihenfolge: Name von employee1 (String), Gehalt von employee1 (int), Name von employee2 (String), Gehalt von employee2 (int), Name der Unterabteilung (String), Name von employee3 (String), Gehalt von employee3 (int).
Zum Beispiel würde Ihre Ausgabe bei den Eingaben Alice, 75000, Bob, 65000, QA, Charlie, 55000 wie folgt aussehen:
Engineering Department
Alice: $75000
Bob: $65000
QA Department
Charlie: $55000
Total Salary: $195000Beachten Sie, wie dieselben Methoden showDetails und getSalary nahtlos funktionieren, egal ob sie für einen einzelnen Mitarbeiter oder eine ganze Abteilung mit verschachtelten Unterabteilungen aufgerufen werden. Die Composite-Struktur übernimmt die Rekursion automatisch, sodass Sie die gesamte Hierarchie einheitlich behandeln können!
Spickzettel
Das Composite Pattern (Kompositum-Muster) ist ein Strukturmuster, das Objekte zu Baumstrukturen zusammenfügt, um Teil-Ganzes-Hierarchien darzustellen. Es ermöglicht die einheitliche Behandlung von Einzelobjekten und Kompositionen über eine gemeinsame Schnittstelle.
Das Muster besteht aus drei Schlüsselelementen:
- Component: Schnittstelle, die gemeinsame Operationen definiert
- Leaf: Einzelobjekte, die die Komponentenschnittstelle implementieren
- Composite: Container-Objekte, die Kinder enthalten und Operationen an diese delegieren
Beispielhafte Implementierung eines Dateisystems:
// Component-Schnittstelle
interface FileComponent {
void display(String indent);
int getSize();
}
// Leaf-Klasse
class File implements FileComponent {
private String name;
private int size;
public File(String name, int size) {
this.name = name;
this.size = size;
}
public void display(String indent) {
System.out.println(indent + name + " (" + size + "KB)");
}
public int getSize() {
return size;
}
}
// Composite-Klasse
class Folder implements FileComponent {
private String name;
private List<FileComponent> children = new ArrayList<>();
public Folder(String name) {
this.name = name;
}
public void add(FileComponent component) {
children.add(component);
}
public void display(String indent) {
System.out.println(indent + name + "/");
for (FileComponent child : children) {
child.display(indent + " ");
}
}
public int getSize() {
return children.stream().mapToInt(FileComponent::getSize).sum();
}
}Verwendung der Kompositstruktur:
Folder root = new Folder("Documents");
root.add(new File("resume.pdf", 150));
Folder photos = new Folder("Photos");
photos.add(new File("vacation.jpg", 2000));
root.add(photos);
root.display("");
System.out.println("Total: " + root.getSize() + "KB");Das Kompositum delegiert Operationen rekursiv an seine Kinder. Clients interagieren mit dem Baum, ohne zu wissen, ob sie mit einem Blatt oder einem Kompositum arbeiten.
Anwendungsfälle: Organigramme, UI-Komponenten, Menüsysteme, Dateisysteme – jede Hierarchie, in der Gruppen und Einzelpersonen identisch behandelt werden müssen.
Probier es selbst
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Eingaben lesen
String emp1Name = scanner.nextLine();
int emp1Salary = Integer.parseInt(scanner.nextLine());
String emp2Name = scanner.nextLine();
int emp2Salary = Integer.parseInt(scanner.nextLine());
String subDeptName = scanner.nextLine();
String emp3Name = scanner.nextLine();
int emp3Salary = Integer.parseInt(scanner.nextLine());
// TODO: Erstelle die Abteilung "Engineering" als Wurzel (Root)
// TODO: Erstelle und füge zwei Mitarbeiter zu Engineering hinzu, unter Verwendung der Daten von emp1 und emp2
// TODO: Erstelle eine Unterabteilung unter Verwendung von subDeptName
// TODO: Erstelle und füge einen Mitarbeiter zur Unterabteilung hinzu, unter Verwendung der Daten von emp3
// TODO: Füge die Unterabteilung zu Engineering hinzu
// TODO: Rufe showDetails("") für die Abteilung Engineering auf
// TODO: Gib das Gesamtgehalt im Format: Total Salary: $[amount] aus
}
}
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üsselwortMethodenFelder (Attribute)Konstruktor-MethodeKonstruktor-ÜberladungRückblick – Einfacher Taschenrechner4Vererbung
Grundlagen der Vererbung (extends)Das super-SchlüsselwortMethoden überschreiben (@Override)Konstruktor-VerkettungDie Object-KlasseEinfache & mehrstufige VererbungWarum keine Mehrfachvererbung von KlassenZusammenfassung - Mitarbeiter-Hierarchie7Spezielle Methoden & Object-Klasse
toString()-Methodeequals() und hashCode()clone()-MethodecompareTo() und ComparableComparator-InterfaceZusammenfassung – Benutzerdefiniertes Sortieren2Zugriffsmodifikatoren & Kapselung
Überblick über ZugriffsebenenGetter- und Setter-MethodenInformation HidingDas final-SchlüsselwortRückblick – Bankkonto-Manager5Polymorphismus
Grundlagen der MethodenüberladungMethodenüberschreibung (Laufzeit)Upcasting und DowncastingDer instanceof-OperatorAbstrakte Klassen und MethodenZusammenfassung – Formenrechner8Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungAggregation vs. KompositionInnere, geschachtelte & anonyme KlassenEnums und Enum-MethodenRecords (Java 16+)Sealed Classes (Java 17+)11Design Patterns Teil 1
Einführung in Design PatternsSingleton-PatternFactory-PatternBuilder-PatternObserver-PatternStrategy-Pattern14Abschlussherausforderungen
E-Learning-PlattformBanksystemSpielcharakter-EntwicklungFahrzeugvermietung3Klasseneigenschaften & Statische Member
Instanz- vs. statische VariablenStatische MethodenStatische BlöckeKonstanten (static final)Zusammenfassung – Zähler & Utility6Interfaces & Abstrakte Klassen
Einführung in InterfacesInterfaces implementierenImplementierung mehrerer InterfacesDefault- & statische Methoden in InterfacesAbstrakte Klassen vs. InterfacesFunktionale InterfacesZusammenfassung - Zahlungssystem9Generics
Einführung in GenericsGenerische KlassenGenerische MethodenEingeschränkte TypparameterWildcards (?, extends, super)Zusammenfassung – Generische Container12Entwurfsmuster Teil 2
Command-MusterAdapter-MusterDecorator-MusterTemplate-Method-MusterState-MusterComposite-MusterIterator-Muster