Composite-Muster
Teil des Abschnitts Objektorientierte Programmierung der Dart-Journey von Coddy — Lektion 99 von 110.
Das Composite-Muster ermöglicht es Ihnen, einzelne Objekte und Gruppen von Objekten einheitlich zu behandeln. Es setzt Objekte zu Baumstrukturen zusammen, in denen sowohl Einzelelemente als auch Sammlungen von Elementen dieselbe Schnittstelle teilen. Dies ist ideal für die Darstellung von Hierarchien wie Dateisystemen, Organigrammen oder UI-Komponenten.
Das Muster hat zwei Arten von Teilnehmern: Leaf-Knoten (einzelne Objekte ohne Kinder) und Composite-Knoten (Container, die andere Komponenten enthalten). Beide implementieren ein gemeinsames Component-Interface:
abstract class FileSystemItem {
String get name;
int getSize();
void display([String indent = '']);
}
class File implements FileSystemItem {
@override
final String name;
final int size;
File(this.name, this.size);
@override
int getSize() => size;
@override
void display([String indent = '']) {
print('$indent- $name ($size KB)');
}
}
class Folder implements FileSystemItem {
@override
final String name;
final List<FileSystemItem> _children = [];
Folder(this.name);
void add(FileSystemItem item) => _children.add(item);
@override
int getSize() => _children.fold(0, (sum, item) => sum + item.getSize());
@override
void display([String indent = '']) {
print('$indent+ $name/');
for (var child in _children) {
child.display('$indent ');
}
}
}
void main() {
var docs = Folder('Documents');
docs.add(File('resume.pdf', 150));
docs.add(File('photo.jpg', 2400));
var projects = Folder('Projects');
projects.add(File('main.dart', 25));
docs.add(projects);
docs.display();
print('Total: ${docs.getSize()} KB');
}Die Klasse Folder kann sowohl File-Objekte als auch andere Folder-Objekte enthalten. Wenn Sie getSize() für einen Ordner aufrufen, berechnet sie rekursiv die Gesamtgröße seines gesamten Inhalts. Der Client-Code muss nicht wissen, ob er mit einer einzelnen Datei oder einer gesamten Ordnerhierarchie arbeitet – beide reagieren auf dieselben Methoden.
Aufgabe
EinfachLassen Sie uns ein Organigramm-System mit dem Composite-Pattern erstellen! Sie werden eine Struktur aufbauen, in der sowohl einzelne Mitarbeiter als auch Abteilungen (die Mitarbeiter und andere Abteilungen enthalten) einheitlich behandelt werden können - perfekt für die Darstellung von Unternehmenshierarchien.
Sie werden Ihren Code in zwei Dateien organisieren:
organization.dart: Diese Datei enthält Ihr Komponenten-Interface sowie die Leaf- und Composite-Klassen. Erstellen Sie eine abstrakte KlasseOrganizationUnitmit einemnameGetter, einergetSalaryBudget()Methode, die einenintzurückgibt, und einerdisplay([String indent = ''])Methode zur Anzeige der Hierarchie. Erstellen Sie eineEmployeeKlasse (das Leaf), dieOrganizationUnitimplementiert. Sie sollte einen Namen und ein Gehalt haben, wobeigetSalaryBudget()das Gehalt des Mitarbeiters zurückgibt. Diedisplay()Methode sollte[indent]- [name] ($[salary])ausgeben. Erstellen Sie dann eineDepartmentKlasse (das Composite), die ebenfallsOrganizationUnitimplementiert. Sie sollte einen Namen haben und eine Liste vonOrganizationUnitKindern verwalten. Fügen Sie eineadd()Methode hinzu, um Mitarbeiter oder Unterabteilungen aufzunehmen. DiegetSalaryBudget()Methode sollte rekursiv die Budgets aller Kinder summieren. Diedisplay()Methode sollte[indent]+ [name]/ausgeben, gefolgt von der Anzeige jedes Kindes mit erhöhter Einrückung (fügen Sie zwei Leerzeichen hinzu).main.dart: Importieren Sie Ihre Organisationsdatei und bauen Sie eine Unternehmensstruktur auf. Erstellen Sie eineEngineeringAbteilung mit zwei Mitarbeitern:Alicemit einem Gehalt von80000undBobmit einem Gehalt von75000. Erstellen Sie eineQAAbteilung mit einer Mitarbeiterin:Carolmit einem Gehalt von65000. Erstellen Sie nun eineTechnologyAbteilung und fügen Sie sowohlEngineeringals auchQAals Unterabteilungen hinzu. Rufen Siedisplay()für die Technology-Abteilung auf und geben Sie dannTotal Budget: $gefolgt vom gesamten Gehaltsbudget aus.
Das Schöne am Composite-Pattern ist, dass Technology.getSalaryBudget() automatisch die Summe über alle verschachtelten Abteilungen und Mitarbeiter berechnet - der Client-Code muss die interne Struktur nicht kennen!
Erwartete Ausgabe:
+ Technology/
+ Engineering/
- Alice ($80000)
- Bob ($75000)
+ QA/
- Carol ($65000)
Total Budget: $220000Spickzettel
Das Composite-Muster ermöglicht es Ihnen, einzelne Objekte und Gruppen von Objekten einheitlich zu behandeln, indem Sie diese in Baumstrukturen zusammensetzen, in denen sowohl Einzelelemente als auch Sammlungen dieselbe Schnittstelle teilen.
Das Muster besteht aus:
- Component: Eine abstrakte Schnittstelle, die gemeinsame Operationen definiert
- Leaf: Einzelne Objekte ohne Kindelemente
- Composite: Container, die andere Komponenten enthalten (Blätter oder Komposita)
Beispiel: Dateisystem
abstract class FileSystemItem {
String get name;
int getSize();
void display([String indent = '']);
}
class File implements FileSystemItem {
@override
final String name;
final int size;
File(this.name, this.size);
@override
int getSize() => size;
@override
void display([String indent = '']) {
print('$indent- $name ($size KB)');
}
}
class Folder implements FileSystemItem {
@override
final String name;
final List<FileSystemItem> _children = [];
Folder(this.name);
void add(FileSystemItem item) => _children.add(item);
@override
int getSize() => _children.fold(0, (sum, item) => sum + item.getSize());
@override
void display([String indent = '']) {
print('$indent+ $name/');
for (var child in _children) {
child.display('$indent ');
}
}
}
void main() {
var docs = Folder('Documents');
docs.add(File('resume.pdf', 150));
docs.add(File('photo.jpg', 2400));
var projects = Folder('Projects');
projects.add(File('main.dart', 25));
docs.add(projects);
docs.display();
print('Total: ${docs.getSize()} KB');
}Die Klasse Folder kann sowohl File-Objekte als auch andere Folder-Objekte enthalten. Wenn Sie getSize() für einen Ordner aufrufen, berechnet dieser rekursiv die Gesamtgröße seines gesamten Inhalts. Der Client-Code behandelt einzelne Dateien und ganze Ordnerhierarchien einheitlich.
Probier es selbst
import 'organization.dart';
void main() {
// TODO: Erstelle eine Engineering-Abteilung
// TODO: Füge Alice mit einem Gehalt von 80000 hinzu
// TODO: Füge Bob mit einem Gehalt von 75000 hinzu
// TODO: Erstelle eine QA-Abteilung
// TODO: Füge Carol mit einem Gehalt von 65000 hinzu
// TODO: Erstelle eine Technology-Abteilung
// TODO: Füge Engineering und QA als Unterabteilungen hinzu
// TODO: Rufe display() für die Technology-Abteilung auf
// TODO: Drucke "Total Budget: $" gefolgt vom gesamten Gehaltsbudget
}
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 DateienBibliotheken & ImportsEinführung in die OOPKlassen vs. ObjekteDas this-SchlüsselwortMethodenInstanzvariablenKonstruktor-GrundlagenWiederholung – Einfacher Taschenrechner4Null-Sicherheit
Einführung in Null-SicherheitNullable vs. Non-NullableDie ? und ! OperatorenLate-Schlüsselwort & Null-SicherheitNull-aware OperatorenNull-Sicherheit in KlassenZusammenfassung – Benutzerprofil-System7Abstrakte Klassen & Interfaces
Abstrakte KlassenAbstrakte MethodenInterfaces in DartImplizite InterfacesImplementieren vs. ErweiternMehrfache InterfacesRückblick – Shape Calculator10Collections & Generics
Überblick über List, Set und MapTypsichere CollectionsGenerische KlassenGenerische MethodenGenerische ConstraintsIterable & IteratorZusammenfassung – Generische Speicherung13Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungExtension MethodsCallable ClassesSealed Classes (Dart 3)Records (Dart 3)Patterns & Matching (3.0)Enums mit Methoden16Projekt: Bibliotheksverwaltung
ProjektübersichtBuch- und Benutzerklassen2Konstruktoren in Dart
Standard-KonstruktorBenannte KonstruktorenInitialisierungslistenKonstante KonstruktorenFactory-KonstruktorenWeiterleitende KonstruktorenZusammenfassung – Shape Builder5Kapselung
Öffentliche vs. private MemberDie _ Präfix-KonventionSichtbarkeit auf Library-EbeneGetter & Setter im DetailInformation HidingRückblick – Schülerdatensätze8Mixins
Einführung in MixinsMixins erstellenMehrere Mixins verwendenDas on-Schlüsselwort in MixinsMixin vs. VererbungMixin vs. InterfaceZusammenfassung - Tiersystem11Spezielle Methoden
toString() überschreibenhashCode & == überschreibenComparable-Interfacecall()-MethodenoSuchMethod überschreibenZusammenfassung – Eigene Collection14Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFactory-MusterObserver-MusterStrategy-Muster3Klasseneigenschaften
Instanz- vs. statische MemberFinal & Const FelderLate-VariablenStatische Methoden & FelderGetter und SetterZusammenfassung – Bankkonto-Manager6Vererbung
Grundlagen der VererbungDas super-SchlüsselwortMethoden überschreibenDie @override-AnnotationDas final-Klassen-SchlüsselwortKonstruktoren & VererbungRückblick – Mitarbeiter-Hierarchie9Polymorphismus
Polymorphismus-GrundlagenPolymorphismus über InterfacesTypprüfung zur LaufzeitDie is & as OperatorenDas covariant SchlüsselwortZusammenfassung – Payment Processor12Asynchrone OOP
Futures & async/awaitStreams GrundlagenStream ControllerAsynchrone KonstruktorenAsync in KlassenmethodenZusammenfassung – Data Fetcher15Entwurfsmuster Teil 2
Command-MusterAdapter-MusterDecorator-MusterTemplate-Method-MusterZustandsmusterComposite-MusterRepository-Muster