Ein Blick auf das Composite-Designmuster
Neulich hörte ich Car Talk von National Public Radio , eine beliebte wöchentliche Sendung, in der Anrufer Fragen zu ihren Fahrzeugen stellen. Vor jeder Programmpause bitten die Moderatoren der Show die Anrufer, 1-800-CAR-TALK zu wählen, was 1-800-227-8255 entspricht. Ersteres ist natürlich viel leichter zu merken als Letzteres, auch weil die Wörter "CAR TALK" zusammengesetzt sind: zwei Wörter, die sieben Ziffern darstellen. Menschen finden es im Allgemeinen einfacher, mit Verbundwerkstoffen umzugehen, als mit ihren einzelnen Komponenten. Ebenso ist es bei der Entwicklung objektorientierter Software häufig praktisch, Verbundwerkstoffe so zu bearbeiten, wie Sie einzelne Komponenten bearbeiten. Diese Prämisse repräsentiert das Grundprinzip des zusammengesetzten Entwurfsmusters.das Thema dieser Java Design Patterns Rate.
Das zusammengesetzte Muster
Bevor wir uns mit dem zusammengesetzten Muster befassen, muss ich zuerst zusammengesetzte Objekte definieren : Objekte, die andere Objekte enthalten; Beispielsweise kann eine Zeichnung aus grafischen Grundelementen wie Linien, Kreisen, Rechtecken, Text usw. bestehen.
Java-Entwickler benötigen das Composite-Muster, da wir Composites häufig genauso bearbeiten müssen, wie wir primitive Objekte bearbeiten. Beispielsweise müssen grafische Grundelemente wie Linien oder Text gezeichnet, verschoben und in der Größe geändert werden. Wir möchten aber auch die gleiche Operation für Verbundwerkstoffe ausführen, z. B. Zeichnungen, die aus diesen Grundelementen bestehen. Im Idealfall möchten wir Operationen an primitiven Objekten und Verbundwerkstoffen auf genau dieselbe Weise ausführen, ohne zwischen beiden zu unterscheiden. Wenn wir zwischen primitiven Objekten und Verbundwerkstoffen unterscheiden müssen, um dieselben Operationen für diese beiden Objekttypen auszuführen, wird unser Code komplexer und schwieriger zu implementieren, zu warten und zu erweitern.
In Design Patterns beschreiben die Autoren das Composite-Muster folgendermaßen:
Verfassen Sie Objekte zu Baumstrukturen, um Teil-Ganz-Hierarchien darzustellen. Mit Composite können Clients einzelne Objekte und Objektzusammensetzungen einheitlich behandeln.Das Implementieren des zusammengesetzten Musters ist einfach. Zusammengesetzte Klassen erweitern eine Basisklasse, die primitive Objekte darstellt. Abbildung 1 zeigt ein Klassendiagramm, das die Struktur des zusammengesetzten Musters veranschaulicht.

Im Klassendiagramm von Abbildung 1 habe ich Klassennamen aus der Diskussion über zusammengesetzte Muster von Design Pattern verwendet : Component
Stellt eine Basisklasse (oder möglicherweise eine Schnittstelle) für primitive Objekte dar und Composite
stellt eine zusammengesetzte Klasse dar. Beispielsweise Component
könnte die Klasse eine Basisklasse für grafische Grundelemente darstellen, während die Composite
Klasse eine Drawing
Klasse darstellen könnte. Die Leaf
Klasse von Abbildung 1 repräsentiert ein konkretes primitives Objekt. Zum Beispiel eine Line
Klasse oder eine Text
Klasse. Die Methoden Operation1()
und Operation2()
stellen domänenspezifische Methoden dar, die sowohl von der Component
als auch von der Composite
Klasse implementiert sind.
Die Composite
Klasse verwaltet eine Sammlung von Komponenten. In der Regel werden Composite
Methoden implementiert, indem diese Sammlung durchlaufen und die entsprechende Methode für jede Component
in der Sammlung aufgerufen wird. Beispielsweise kann eine Drawing
Klasse ihre draw()
Methode folgendermaßen implementieren :
// Diese Methode ist eine zusammengesetzte Methode public void draw () {// Durchlaufe die Komponenten für (int i = 0; i <getComponentCount (); ++ i) {// Rufe einen Verweis auf die Komponente ab und rufe ihre Zeichnung auf Methode Component component = getComponent (i); component.draw (); }}
Für jede in der Component
Klasse implementierte Methode implementiert die Klasse Composite
eine Methode mit derselben Signatur, die über die Komponenten des Verbundwerkstoffs iteriert, wie in der draw()
oben aufgeführten Methode dargestellt.
Die Composite
Klasse erweitert die Component
Klasse, sodass Sie einen Verbund an eine Methode übergeben können, die eine Komponente erwartet. Betrachten Sie beispielsweise die folgende Methode:
// Diese Methode ist in einer Klasse implementiert, die nichts mit den Klassen // Component und Composite zu tun hat. Public void repaint (Component component) {// Die Komponente kann ein Composite sein, aber da sie // die Component-Klasse erweitert, muss diese Methode nicht // zwischen Komponenten und Verbundwerkstoffen unterscheiden component.draw (); }}
Die vorhergehende Methode wird einer Komponente übergeben - entweder einer einfachen Komponente oder einem Composit - und ruft dann die draw()
Methode dieser Komponente auf . Da die Composite
Klasse erweitert wird Component
, muss die repaint()
Methode nicht zwischen Komponenten und Verbundwerkstoffen unterscheiden. Sie ruft einfach die draw()
Methode für die Komponente (oder den Verbundwerkstoff) auf.
Das Klassendiagramm für zusammengesetzte Muster in Abbildung 1 zeigt ein Problem mit dem Muster: Sie müssen zwischen Komponenten und Verbundwerkstoffen unterscheiden, wenn Sie auf a verweisen Component
, und Sie müssen eine zusammensetzungsspezifische Methode aufrufen, z addComponent()
. Normalerweise erfüllen Sie diese Anforderung, indem Sie isComposite()
der Component
Klasse eine Methode hinzufügen, z . Diese Methode gibt false
für Komponenten zurück und wird in der zurückzugebenden Composite
Klasse überschrieben true
. Darüber hinaus müssen Sie den Component
Verweis auf eine Composite
Instanz wie folgt umwandeln:
... if (component.isComposite ()) {Composite Composite = (Composite) Komponente; osite.addComponent (someComponentThatCouldBeAComposite); } ...
Beachten Sie, dass der addComponent()
Methode eine Component
Referenz übergeben wird, die entweder eine primitive Komponente oder eine zusammengesetzte Komponente sein kann. Da diese Komponente ein Verbund sein kann, können Sie Komponenten zu einer Baumstruktur zusammensetzen, wie aus dem oben genannten Zitat aus Entwurfsmuster hervorgeht .
Abbildung 2 zeigt eine alternative Implementierung eines zusammengesetzten Musters.
Wenn Sie das zusammengesetzte Muster von Abbildung 2 implementieren, müssen Sie nie zwischen Komponenten und Verbundwerkstoffen unterscheiden und keinen Component
Verweis auf eine Composite
Instanz erstellen. Das oben aufgeführte Codefragment reduziert sich also auf eine einzelne Zeile:
... component.addComponent (someComponentThatCouldBeAComposite); ...
Aber was sollte der tun , wenn sich die Component
Referenz im vorhergehenden Codefragment nicht auf a bezieht ? Dies ist ein Hauptstreitpunkt bei der Implementierung des zusammengesetzten Musters in Abbildung 2. Da primitive Komponenten keine anderen Komponenten enthalten, ist das Hinzufügen einer Komponente zu einer anderen Komponente nicht sinnvoll. Daher kann die Methode entweder unbeaufsichtigt fehlschlagen oder eine Ausnahme auslösen. In der Regel wird das Hinzufügen einer Komponente zu einer anderen primitiven Komponente als Fehler angesehen. Daher ist das Auslösen einer Ausnahme möglicherweise die beste Vorgehensweise.Composite
addComponent()
Component.addComponent()
Welche Implementierung eines zusammengesetzten Musters - die in Abbildung 1 oder die in Abbildung 2 - funktioniert am besten? Dies ist unter Composite-Pattern-Implementierern immer ein Thema, über das große Debatten geführt werden. Design Patterns bevorzugt die Implementierung in Abbildung 2, da Sie niemals zwischen Komponenten und Containern unterscheiden müssen und niemals eine Umwandlung durchführen müssen. Persönlich bevorzuge ich die Implementierung von Abbildung 1, da ich eine starke Abneigung gegen die Implementierung von Methoden in einer Klasse habe, die für diesen Objekttyp keinen Sinn ergeben.
Nachdem Sie das zusammengesetzte Muster verstanden haben und wissen, wie Sie es implementieren können, untersuchen wir ein Beispiel für ein zusammengesetztes Muster mit dem JSP-Framework (Apache Struts JavaServer Pages).
Das zusammengesetzte Muster und die Strebenfliesen
Das Apache Struts-Framework enthält eine JSP-Tag-Bibliothek, die als Tiles bezeichnet wird und mit der Sie eine Webseite aus mehreren JSPs erstellen können. Tiles ist eine Implementierung des CompositeView-Musters J2EE (Java 2 Platform, Enterprise Edition), das selbst auf dem Composite-Muster Design Patterns basiert . Bevor wir die Relevanz des zusammengesetzten Musters für die Tiles-Tag-Bibliothek diskutieren, wollen wir zunächst die Gründe für Tiles und deren Verwendung überprüfen. Wenn Sie bereits mit Strebenkacheln vertraut sind, können Sie die folgenden Abschnitte überfliegen und mit dem Lesen unter "Verwenden des Verbundmusters mit Strebenkacheln" beginnen.
Hinweis: Weitere Informationen zum J2EE CompositeView-Muster finden Sie in meinem Artikel " Webanwendungskomponenten, die mit Composite View einfach gemacht wurden " ( JavaWorld, Dezember 2001).
Designer erstellen häufig Webseiten mit einer Reihe diskreter Bereiche. Beispielsweise umfasst die Webseite von Abbildung 3 eine Seitenleiste, eine Kopfzeile, einen Inhaltsbereich und eine Fußzeile.
Websites enthalten häufig mehrere Webseiten mit identischen Layouts, z. B. das Seitenleisten- / Kopf- / Inhalts- / Fußzeilenlayout in Abbildung 3. Mit Struts Tiles können Sie sowohl Inhalt als auch Layout auf mehreren Webseiten wiederverwenden. Bevor wir diese Wiederverwendung diskutieren, wollen wir sehen, wie das Layout von Abbildung 3 traditionell nur mit HTML implementiert wird.
Implementieren Sie komplexe Layouts von Hand
Beispiel 1 zeigt, wie Sie die Webseite von Abbildung 3 mit HTML implementieren können:
Beispiel 1. Ein komplexes Layout, das von Hand implementiert wurde
Komplexe Layouts von Hand implementieren <% - Eine Tabelle enthält den gesamten Inhalt dieser Seite -%>
|
|
Die vorhergehende JSP hat zwei Hauptnachteile: Erstens ist der Inhalt der Seite in die JSP eingebettet, sodass Sie nichts davon wiederverwenden können, obwohl Seitenleiste, Kopf- und Fußzeile auf vielen Webseiten wahrscheinlich gleich sind. Zweitens ist das Layout der Seite auch in diese JSP eingebettet, sodass Sie es ebenfalls nicht wiederverwenden können, obwohl viele andere Webseiten auf derselben Website dasselbe Layout verwenden. Wir können die Aktion verwenden, um den ersten Nachteil zu beheben, wie ich als nächstes diskutiere.
Implementieren Sie komplexe Layouts mit JSP-Includes
Beispiel 2 zeigt eine Implementierung der Webseite von Abbildung 3, die Folgendes verwendet :