Menu
Coddy logo textTech

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.

challenge icon

Aufgabe

Einfach

Lassen 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 Klasse OrganizationUnit mit einem name Getter, einer getSalaryBudget() Methode, die einen int zurückgibt, und einer display([String indent = '']) Methode zur Anzeige der Hierarchie. Erstellen Sie eine Employee Klasse (das Leaf), die OrganizationUnit implementiert. Sie sollte einen Namen und ein Gehalt haben, wobei getSalaryBudget() das Gehalt des Mitarbeiters zurückgibt. Die display() Methode sollte [indent]- [name] ($[salary]) ausgeben. Erstellen Sie dann eine Department Klasse (das Composite), die ebenfalls OrganizationUnit implementiert. Sie sollte einen Namen haben und eine Liste von OrganizationUnit Kindern verwalten. Fügen Sie eine add() Methode hinzu, um Mitarbeiter oder Unterabteilungen aufzunehmen. Die getSalaryBudget() Methode sollte rekursiv die Budgets aller Kinder summieren. Die display() 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 eine Engineering Abteilung mit zwei Mitarbeitern: Alice mit einem Gehalt von 80000 und Bob mit einem Gehalt von 75000. Erstellen Sie eine QA Abteilung mit einer Mitarbeiterin: Carol mit einem Gehalt von 65000. Erstellen Sie nun eine Technology Abteilung und fügen Sie sowohl Engineering als auch QA als Unterabteilungen hinzu. Rufen Sie display() für die Technology-Abteilung auf und geben Sie dann Total 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: $220000

Spickzettel

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