Menu
Coddy logo textTech

Kompositumsmuster

Teil des Abschnitts Objektorientierte Programmierung der C#-Journey von Coddy. Lektion 61 von 70.

Das Composite-Muster ist ein Strukturmuster, mit dem du Objekte zu Baumstrukturen zusammensetzen und einzelne Objekte sowie Objektgruppen einheitlich behandeln kannst. Es eignet sich perfekt zur Darstellung von Teil-Ganzes-Hierarchien, in denen du über dieselbe Schnittstelle sowohl mit einzelnen Elementen als auch mit Sammlungen arbeiten möchtest.

Das Muster hat drei zentrale Komponenten: eine Komponentenschnittstelle, die gemeinsame Operationen definiert, Blattklassen, die einzelne Objekte darstellen, und Kompositklassen, die Kinder enthalten und dieselbe Schnittstelle implementieren:

public interface IFileSystemItem
{
    string Name { get; }
    int GetSize();
}

public class File : IFileSystemItem
{
    public string Name { get; }
    private int _size;
    
    public File(string name, int size)
    {
        Name = name;
        _size = size;
    }
    
    public int GetSize() => _size;
}

public class Folder : IFileSystemItem
{
    public string Name { get; }
    private List<IFileSystemItem> _items = new List<IFileSystemItem>();
    
    public Folder(string name) => Name = name;
    
    public void Add(IFileSystemItem item) => _items.Add(item);
    
    public int GetSize() => _items.Sum(item => item.GetSize());
}

Das Schöne an diesem Muster ist, dass Clients nicht wissen müssen, ob sie mit einer einzelnen Datei oder einer gesamten Ordnerhierarchie arbeiten:

var root = new Folder("root");
root.Add(new File("doc.txt", 100));

var subFolder = new Folder("images");
subFolder.Add(new File("photo.jpg", 500));
subFolder.Add(new File("icon.png", 50));

root.Add(subFolder);

Console.WriteLine(root.GetSize());  // 650

Das Composite-Muster eignet sich ideal für Dateisysteme, Bäume von UI-Komponenten, Organigramme oder jede Struktur, in der Container und Inhalte auf dieselbe Weise behandelt werden sollen. Es vereinfacht den Client-Code, indem es die Notwendigkeit beseitigt, zwischen einfachen und komplexen Elementen zu unterscheiden.

challenge icon

Aufgabe

Einfach

Erstellen wir ein Organigrammsystem mithilfe des Composite-Musters. Du erstellst eine Struktur, in der sowohl einzelne Mitarbeiter als auch Abteilungen (die Mitarbeiter oder andere Abteilungen enthalten) einheitlich behandelt werden können, sodass du die gesamten Gehaltskosten auf jeder Ebene der Hierarchie berechnen kannst.

Du organisierst deinen Code über drei Dateien:

  • OrgComponent.cs: Definiere ein IOrgComponent-Interface im Namespace Organization mit zwei Mitgliedern: einer Name-Eigenschaft (string) und einer GetSalary()-Methode, die eine Ganzzahl zurückgibt. Dieses Interface stellt den gemeinsamen Vertrag dar, dem sowohl einzelne Mitarbeiter als auch Abteilungen folgen.
  • OrgClasses.cs: Erstelle zwei Klassen im selben Namespace:
    • Employee – eine Blattklasse, die einen einzelnen Mitarbeiter darstellt. Sie nimmt in ihrem Konstruktor einen Namen und ein Gehalt entgegen und implementiert das Interface, indem sie ihr eigenes Gehalt zurückgibt.
    • Department – eine Kompositklasse, die eine Gruppe darstellt. Sie nimmt in ihrem Konstruktor einen Namen entgegen und verwaltet eine Liste von IOrgComponent-Elementen. Füge eine Add(IOrgComponent component)-Methode hinzu, um Mitglieder hinzuzufügen. Ihre GetSalary()-Methode soll die Summe der Gehälter aller ihrer Mitglieder zurückgeben.
  • Program.cs: Erstelle anhand der Eingaben eine Organisationsstruktur und berechne das Gesamtgehalt. Du erstellst Abteilungen und Mitarbeiter, verschachtelst sie passend und demonstrierst, wie das Composite-Muster es dir ermöglicht, die gesamte Hierarchie einheitlich zu behandeln.

Du erhältst die folgenden Eingaben:

  • Die Anzahl der zu erstellenden Komponenten
  • Für jede Komponente: der Typ (employee oder department), gefolgt vom Namen und bei Mitarbeitern ihrem Gehalt. Für Abteilungen erhältst du außerdem den Namen der übergeordneten Abteilung (oder root, wenn es sich um eine Abteilung der obersten Ebene handelt).

Eingabeformat für jede Komponente:

employee
{name}
{salary}
{parent_department}

department
{name}
{parent_department}

Gib nach dem Erstellen der Struktur das Gesamtgehalt der Root-Abteilung im Format {root_name} Total Salary: {amount} aus.

Wenn die Eingaben beispielsweise folgendermaßen lauten:

5
department
Engineering
root
employee
Alice
5000
Engineering
employee
Bob
4500
Engineering
department
QA
Engineering
employee
Charlie
4000
QA

Die Ausgabe sollte folgendermaßen aussehen:

Engineering Total Salary: 13500

Die Abteilung Engineering enthält Alice (5000), Bob (4500) und die Abteilung QA. QA enthält Charlie (4000). Wenn du GetSalary() für Engineering aufrufst, wird die Gesamtsumme aller verschachtelten Komponenten rekursiv berechnet – dies demonstriert, wie das Composite-Muster es dir ermöglicht, über eine einfache, einheitliche Schnittstelle mit komplexen Baumstrukturen zu arbeiten!

Probier es selbst

using System;
using System.Collections.Generic;
using Organization;

class Program
{
    public static void Main(string[] args)
    {
        int n = Convert.ToInt32(Console.ReadLine());
        
        // TODO: Erstelle ein Dictionary, um Abteilungen nach Namen zu speichern
        // Dies hilft dir dabei, übergeordnete Abteilungen beim Hinzufügen von Komponenten zu finden
        Dictionary<string, Department> departments = new Dictionary<string, Department>();
        
        // TODO: Behalte die oberste Abteilung (root department) im Auge
        Department root = null;
        
        for (int i = 0; i < n; i++)
        {
            string type = Console.ReadLine();
            
            if (type == "department")
            {
                string name = Console.ReadLine();
                string parent = Console.ReadLine();
                
                // TODO: Erstelle die Abteilung
                // TODO: Füge sie dem Dictionary departments hinzu
                // TODO: Wenn parent "root" ist, ist dies die oberste Abteilung
                // TODO: Andernfalls finde die übergeordnete Abteilung und füge diese Abteilung dort hinzu
            }
            else if (type == "employee")
            {
                string name = Console.ReadLine();
                int salary = Convert.ToInt32(Console.ReadLine());
                string parent = Console.ReadLine();
                
                // TODO: Erstelle den Mitarbeiter
                // TODO: Finde die übergeordnete Abteilung und füge den Mitarbeiter dort hinzu
            }
        }
        
        // TODO: Gib das Gesamtgehalt der obersten Abteilung aus
        // Format: "{root_name} Total Salary: {amount}"
        Console.WriteLine($"{root.Name} Total Salary: {root.GetSalary()}");
    }
}
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-C#-Compiler