Menu
Coddy logo textTech

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.

challenge icon

Aufgabe

Einfach

Lassen 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 und getSalary(), das das Gesamtgehalt als Ganzzahl zurückgibt.
  • Employee.java: Erstellen Sie die Leaf-Klasse (Blatt-Klasse), die einzelne Mitarbeiter repräsentiert. Ein Employee hat einen Namen (String) und ein Gehalt (int), die beide über den Konstruktor gesetzt werden. Wenn showDetails aufgerufen wird, sollte es [indent][name]: $[salary] ausgeben. Die Methode getSalary gibt einfach das Gehalt des Mitarbeiters zurück.
  • Department.java: Erstellen Sie die Composite-Klasse (Zusammengesetzte Klasse), die sowohl Mitarbeiter als auch Unterabteilungen enthalten kann. Eine Department hat einen Namen (String) und verwaltet eine Liste von OrganizationComponent-Kindern. Sie sollte eine Methode add(OrganizationComponent component) haben, um Mitglieder hinzuzufügen. Wenn showDetails aufgerufen wird, gibt sie [indent][name] Department aus und ruft dann showDetails für jedes Kind mit erhöhter Einrückung (fügen Sie zwei Leerzeichen hinzu) auf. Die Methode getSalary gibt 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 Format Total 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: $195000

Beachten 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
    }
}
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