Kompositum-Muster
Teil des Abschnitts Objektorientierte Programmierung der C#-Journey von Coddy — Lektion 61 von 70.
Das Composite-Muster ist ein Strukturmuster, mit dem Sie Objekte zu Baumstrukturen zusammenfügen und einzelne Objekte sowie Gruppen von Objekten einheitlich behandeln können. Es eignet sich hervorragend zur Darstellung von Teil-Ganzes-Hierarchien, bei denen Sie sowohl mit einzelnen Elementen als auch mit Sammlungen über dieselbe Schnittstelle arbeiten möchten.
Das Muster besteht aus drei Schlüsselkomponenten: einer Komponentenschnittstelle, die gemeinsame Operationen definiert, Leaf-Klassen, die einzelne Objekte darstellen, und Composite-Klassen, die Kindelemente 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()); // 650Das Composite-Muster ist ideal für Dateisysteme, UI-Komponentenbäume, Organigramme oder jede Struktur, bei der Container und Inhalte auf die gleiche Weise behandelt werden sollten. Es vereinfacht den Client-Code, da nicht mehr zwischen einfachen und komplexen Elementen unterschieden werden muss.
Aufgabe
EinfachLassen Sie uns ein Organigramm-System mit dem Composite-Muster erstellen. Sie werden eine Struktur entwerfen, in der sowohl einzelne Mitarbeiter als auch Abteilungen (die Mitarbeiter oder andere Abteilungen enthalten) einheitlich behandelt werden können, sodass Sie die Gesamtkosten für Gehälter auf jeder Ebene der Hierarchie berechnen können.
Sie werden Ihren Code über drei Dateien organisieren:
OrgComponent.cs: Definieren Sie einIOrgComponent-Interface im NamespaceOrganizationmit zwei Membern: einerName-Eigenschaft (string) und einerGetSalary()-Methode, die einen Integer zurückgibt. Dieses Interface stellt den gemeinsamen Vertrag dar, dem sowohl einzelne Mitarbeiter als auch Abteilungen folgen werden.OrgClasses.cs: Erstellen Sie zwei Klassen im selben Namespace:Employee- eine Leaf-Klasse, die einen einzelnen Mitarbeiter darstellt. Sie nimmt einen Namen und ein Gehalt im Konstruktor entgegen und implementiert das Interface, indem sie ihr eigenes Gehalt zurückgibt.Department- eine Composite-Klasse, die eine Gruppe darstellt. Sie nimmt einen Namen im Konstruktor entgegen und verwaltet eine Liste vonIOrgComponent-Elementen. Fügen Sie eineAdd(IOrgComponent component)-Methode hinzu, um Mitglieder hinzuzufügen. IhreGetSalary()-Methode sollte die Summe der Gehälter aller ihrer Mitglieder zurückgeben.
Program.cs: Erstellen Sie eine Organisationsstruktur basierend auf der Eingabe und berechnen Sie das Gesamtgehalt. Sie werden Abteilungen und Mitarbeiter erstellen, diese entsprechend verschachteln und demonstrieren, wie das Composite-Muster es Ihnen ermöglicht, die gesamte Hierarchie einheitlich zu behandeln.
Sie erhalten die folgenden Eingaben:
- Die Anzahl der zu erstellenden Komponenten
- Für jede Komponente: den Typ (
employeeoderdepartment), gefolgt vom Namen und bei Mitarbeitern deren Gehalt. Bei Abteilungen erhalten Sie außerdem den Namen der übergeordneten Abteilung (oderroot, wenn es sich um eine Abteilung auf oberster Ebene handelt).
Eingabeformat für jede Komponente:
employee
{name}
{salary}
{parent_department}
department
{name}
{parent_department}Nachdem Sie die Struktur aufgebaut haben, geben Sie das Gesamtgehalt der Root-Abteilung im Format {root_name} Total Salary: {amount} aus.
Wenn die Eingaben beispielsweise wie folgt lauten:
5
department
Engineering
root
employee
Alice
5000
Engineering
employee
Bob
4500
Engineering
department
QA
Engineering
employee
Charlie
4000
QASollte die Ausgabe wie folgt aussehen:
Engineering Total Salary: 13500Die Abteilung Engineering enthält Alice (5000), Bob (4500) und die Abteilung QA. QA enthält Charlie (4000). Wenn Sie GetSalary() für Engineering aufrufen, wird die Summe aller verschachtelten Komponenten rekursiv berechnet – dies demonstriert, wie das Composite-Muster es Ihnen ermöglicht, mit komplexen Baumstrukturen über ein einfaches, einheitliches Interface zu arbeiten!
Spickzettel
Das Composite-Muster (Kompositum) ist ein Strukturmuster, das Objekte zu Baumstrukturen zusammenfügt und einzelne Objekte sowie Gruppen über dieselbe Schnittstelle einheitlich behandelt. Es ist ideal für Teil-Ganzes-Hierarchien.
Das Muster besteht aus drei Schlüsselkomponenten:
- Component-Schnittstelle: Definiert gemeinsame Operationen
- Leaf-Klassen (Blätter): Repräsentieren einzelne Objekte
- Composite-Klassen (Komposita): Enthalten Kindelemente und implementieren dieselbe Schnittstelle
Beispiel für eine Basisimplementierung:
// Component-Schnittstelle
public interface IFileSystemItem
{
string Name { get; }
int GetSize();
}
// Leaf-Klasse
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;
}
// Composite-Klasse
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());
}Verwendung – Clients arbeiten einheitlich mit einzelnen Elementen und Sammlungen:
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()); // 650Das Composite-Muster ist nützlich für Dateisysteme, UI-Komponentenbäume, Organigramme oder jede Struktur, bei der Container und Inhalte auf die gleiche Weise behandelt werden sollen.
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()}");
}
}
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 DateienNamespaces & DirektivenEinführung in Klassen & ObjekteDas 'this'-SchlüsselwortMethoden und ParameterFelder vs. EigenschaftenKonstruktorenObjekt-InitialisiererZusammenfassung - Einfacher Taschenrechner4Vererbung
Grundlagen der Vererbung (:) SyntaxDas 'base'-SchlüsselwortVirtual & Override SchlüsselwörterVersiegelte KlassenDie 'object'-BasisklasseZusammenfassung – Mitarbeiter-Hierarchie7Fortgeschrittene Funktionen
OperatorüberladungIndexer (this[])ToString() überschreibenErweiterungsmethodenZusammenfassung - Benutzerdefinierte Liste2Eigenschaften & Statische Member
Automatisch implementierte EigenschaftenRead-Only- & Write-Only-EigenschaftenStatische Felder & MethodenStatische KlassenExpression-Bodied Member5Polymorphismus & Interfaces
Compile- vs. Runtime-PolymorphismusInterface vs. abstrakte KlasseMehrfache InterfacesExplizite InterfacesUpcasting & DowncastingRecap – Shape Calculator8Fortgeschrittene OOP-Konzepte
Komposition statt VererbungGenerics (Klassen & Methoden)Delegates und EventsAttribute und ReflectionIDisposable & using-AnweisungDependency Injection Grundlagen11Entwurfsmuster Teil 2
Command-MusterAdapter-MusterDecorator-MusterTemplate-Method-MusterZustandsmusterKompositum-Muster3Klassenarchitektur
Instanz- vs. statische Daten'readonly' & 'const' SchlüsselwörterBacking FieldsZusammenfassung - Bankkonto-Manager6Kapselung
ZugriffsmodifikatorenProperties zur KapselungImplementierung von Data HidingImmutability-MusterRückblick – Studierendendaten