Intent
- Zusammensetzen von Objekten in Baumstrukturen zur Darstellung ganzer Hierarchien. Composite lässt Clients einzelne Objekte und Kompositionen von Objekten einheitlich behandeln.
- Rekursive Komposition
- „Verzeichnisse enthalten Einträge, von denen jeder ein Verzeichnis sein könnte.“
- 1-to-many „hat ein“ die „ist ein“ Hierarchie hinauf
Problem
Anwendung muss eine hierarchische Sammlung von „primitiven“ und „zusammengesetzten“ Objekten bearbeiten. Die Verarbeitung eines primitiven Objekts wird auf eine Weise gehandhabt, die Verarbeitung eines zusammengesetzten Objekts wird anders gehandhabt.
Diskussion
Definieren Sie eine abstrakte Basisklasse (Component), die das Verhalten spezifiziert, das einheitlich für alle primitiven und zusammengesetzten Objekte ausgeübt werden muss. Die Primitiv- und Composite-Klassen werden von der Component-Klasse abgeleitet. Jedes Composite-Objekt „koppelt“ sich nur an den abstrakten Typ Component, während es seine „Kinder“ verwaltet.
Verwenden Sie dieses Muster immer dann, wenn Sie „Composites haben, die Komponenten enthalten, von denen jede ein Composite sein könnte“.
Methoden zur Verwaltung von Kindern sollten normalerweise in der Composite-Klasse definiert werden. Leider erfordert der Wunsch, Primitive und Composites einheitlich zu behandeln, dass diese Methoden in die abstrakte Klasse Component verschoben werden.
Struktur
Komposite, die Komponenten enthalten, von denen jede ein Komposit sein könnte.
Menüs, die Menüpunkte enthalten, von denen jeder ein Menü sein könnte.
Zeilenspalten-GUI-Layoutmanager, die Widgets enthalten, von denen jeder ein Zeilenspalten-GUI-Layoutmanager sein könnte.
Verzeichnisse, die Dateien enthalten, von denen jedes ein Verzeichnis sein kann.
Container, die Elemente enthalten, von denen jedes ein Container sein kann.
Beispiel
Das Composite fasst Objekte zu Baumstrukturen zusammen und lässt den Client einzelne Objekte und Kompositionen einheitlich behandeln. Obwohl dasBeispiel abstrakt ist, sind arithmetische Ausdrücke Composites. Ein arithmetischer Ausdruck besteht aus einem Operanden, einem Operator (+ – * /), und einem weiteren Operanden. Der Operand kann eine Zahl oder ein anderer arithmetischer Ausdruck sein. So sind 2 + 3 und (2 + 3) + (4 * 6) beides gültige Ausdrücke.
Checkliste
- Versichern Sie sich, dass es bei Ihrem Problem darum geht, „Ganzes-Teil“-hierarchische Beziehungen darzustellen.
- Betrachten Sie die Heuristik, „Behälter, die Behälter enthalten, von denen jeder ein Behälter sein könnte.“ Unterteilen Sie Ihre Domänenkonzepte in Containerklassen und Containerklassen.
- Schaffen Sie eine Schnittstelle für den kleinsten gemeinsamen Nenner, die Ihre Container und Containers austauschbar macht. Sie sollte das Verhalten spezifizieren, das einheitlich über alle Containee- und Container-Objekte ausgeübt werden muss.
- Alle Container- und Containee-Klassen deklarieren eine „is a“-Beziehung zur Schnittstelle.
- Alle Containerklassen deklarieren eine eins-zu-viele „has a“-Beziehung zur Schnittstelle.
- Containerklassen nutzen Polymorphismus, um an ihre Containee-Objekte zu delegieren.
- Methoden zur Verwaltung von Kindern sollten normalerweise in der Composite-Klasse definiert werden. Leider kann der Wunsch, Leaf- und Composite-Objekte einheitlich zu behandeln, erfordern, dass diese Methoden in die abstrakte Component-Klasse übertragen werden. Siehe die Gang of Four für eine Diskussion dieser Kompromisse zwischen „Sicherheit“ und „Transparenz“.
Faustregeln
- Composite und Decorator haben ähnliche Strukturdiagramme, was die Tatsache widerspiegelt, dass beide auf rekursiver Komposition beruhen, um eine unbegrenzte Anzahl von Objekten zu organisieren.
- Composite kann mit Iterator durchlaufen werden. Visitor kann eine Operation auf ein Composite anwenden. Composite kann Chain ofResponsibility verwenden, damit Komponenten über ihre Eltern auf globale Eigenschaften zugreifen können. Sie könnte auch Decorator verwenden, um diese Eigenschaften auf Teilen der Komposition außer Kraft zu setzen. Es könnte Observer verwenden, um eine Objektstruktur an eine andere zu binden, und State, um eine Komponente ihr Verhalten ändern zu lassen, wenn sich ihr Zustand ändert.
- Composite kann einen Mediator aus kleineren Teilen durch rekursive Komposition zusammensetzen.
- Decorator wurde entwickelt, um Verantwortlichkeiten zu Objekten hinzuzufügen, ohne sie zu unterklassifizieren. Der Schwerpunkt von Composite liegt nicht auf der Verschönerung, sondern auf der Repräsentation. Daher werden Composite und Decorator oft gemeinsam verwendet.
- Flyweight wird oft mit Composite kombiniert, um gemeinsam genutzte Blattknoten zu implementieren.
Meinungen
Der Sinn des Composite-Musters besteht darin, dass das Composite atomar behandelt werden kann, genau wie ein Blatt. Wenn Sie einIterator-Protokoll bereitstellen wollen, schön und gut, aber ich denke, das liegt außerhalb des Musters selbst. Der Kern dieses Musters ist die Fähigkeit eines Clients, Operationen auf einem Objekt auszuführen, ohne dass er wissen muss, dass es darin viele Objekte gibt.
Die Fähigkeit, eine heterogene Sammlung von Objekten atomar (oder transparent) zu behandeln, erfordert, dass die Schnittstelle für die Verwaltung von Unterobjekten an der Wurzel der Composite-Klassenhierarchie (der abstrakten Komponentenklasse) definiert wird. Diese Entscheidung geht jedoch auf Kosten der Sicherheit, da die Kunden versuchen könnten, sinnlose Dinge wie das Hinzufügen und Entfernen von Objekten aus Blattobjekten zu tun. Wenn Sie hingegen „sicherheitsorientiert“ entwerfen, wird die Schnittstelle für die Verwaltung der Kinder in der Composite-Klasse deklariert, und Sie verlieren an Transparenz, weil Blätter und Composites nun unterschiedliche Schnittstellen haben.
Smalltalk-Implementierungen des Composite-Musters haben die Schnittstelle für die Verwaltung der Komponenten normalerweise nicht in der Component-Schnittstelle, sondern in der Composite-Schnittstelle. C++-Implementierungen neigen dazu, sie in der Komponentenschnittstelle unterzubringen. Dies ist eine äußerst interessante Tatsache, über die ich oft nachdenke. Ich kann Theorien anbieten, um es zu erklären, aber niemand weiß mit Sicherheit, warum es wahr ist.
Meine Komponentenklassen wissen nicht, dass Composites existieren. Sie bieten keine Hilfe zum Navigieren in Composites und auch keine Hilfe zum Ändern des Inhalts eines Composites. Das liegt daran, dass ich möchte, dass die Basisklasse (und alle ihre Derivate) in Kontexten wiederverwendbar sind, die keine Composites erfordern. Wenn ich bei einem Zeiger auf eine Basisklasse unbedingt wissen muss, ob es sich um ein Composite handelt oder nicht, verwende ich dynamic_cast
, um dies herauszufinden. In den Fällen, in denen dynamic_cast
zu kostspielig ist, verwende ich einen Visitor.
Gängige Beschwerde: „Wenn ich die Composite-Schnittstelle in die Composite-Klasse verschiebe, wie soll ich dann eine komplexe Struktur aufzählen (d.h. durchlaufen)?“ Meine Antwort ist, dass ich bei Verhaltensweisen, die sich auf Hierarchien wie im Composite-Muster beziehen, typischerweise Visitor verwende, so dass die Aufzählung kein Problem darstellt – der Visitork weiß in jedem Fall genau, mit welcher Art von Objekt er es zu tun hat. DerVisitor muss nicht für jedes Objekt eine Aufzählungsschnittstelle bereitstellen.
Composite zwingt Sie nicht dazu, alle Komponenten als Composites zu behandeln. Es sagt Ihnen lediglich, dass Sie alle Operationen, die Sie „einheitlich“ behandeln wollen, in die Klasse Component packen sollen. Wenn add, remove und ähnliche Operationen nicht einheitlich behandelt werden können oder dürfen, dann fügen Sie sie nicht in die Basisklasse Component ein. Vergessen Sie übrigens nicht, dass das Struktogramm eines jeden Musters das Muster nicht definiert; es stellt lediglich dar, was unserer Erfahrung nach eine gängige Umsetzung davon ist. Nur weil das Strukturdiagramm von Composite Operationen zur Verwaltung von Unterklassen in der Basisklasse Component zeigt, bedeutet das nicht, dass alle Implementierungen des Musters dasselbe tun müssen.
Unterstützen Sie unsere kostenlose Website und sichern Sie sich das eBook!
- 22 Design Patterns und 8 Prinzipien ausführlich erklärt
- 406 gut strukturierte, leicht lesbare, jargonfreie Seiten
- 228 klare und hilfreiche Illustrationen und Diagramme
- Ein Archiv mit Codebeispielen in 4 Sprachen
- Alle Geräte unterstützt: EPUB/MOBI/PDF-Formate
Erfahren Sie mehr…