Cay Horstmann hat ein gutes Beispiel dafür, wo man sich bewerben kann Besucher in seinem Buch OO Design and patterns . Er fasst das Problem zusammen:
Zusammengesetzte Objekte haben oft eine komplexe Struktur, die aus einzelnen Elementen besteht. Einige Elemente können wiederum untergeordnete Elemente haben. ... Eine Operation auf einem Element besucht seine untergeordneten Elemente, wendet die Operation auf sie an und kombiniert die Ergebnisse. ... Es ist jedoch nicht einfach, neue Operationen zu einem solchen Entwurf hinzuzufügen.
Der Grund dafür, dass dies nicht so einfach ist, liegt darin, dass die Operationen innerhalb der Strukturklassen selbst hinzugefügt werden. Stellen Sie sich zum Beispiel vor, Sie haben ein Dateisystem:
Hier sind einige Operationen (Funktionalitäten), die wir mit dieser Struktur implementieren möchten:
- Anzeige der Namen der Knotenelemente (eine Dateiliste)
- Anzeige der berechneten Größe der Knotenelemente (wobei die Größe eines Verzeichnisses die Größe aller seiner untergeordneten Elemente umfasst)
- usw.
Man könnte jeder Klasse im FileSystem Funktionen hinzufügen, um die Operationen zu implementieren (was in der Vergangenheit auch schon gemacht wurde, da es sehr offensichtlich ist, wie man es macht). Das Problem ist, dass jedes Mal, wenn Sie eine neue Funktionalität hinzufügen (die obige Zeile "usw."), Sie möglicherweise immer mehr Methoden zu den Strukturklassen hinzufügen müssen. Irgendwann, nach einer gewissen Anzahl von Operationen, die Sie Ihrer Software hinzugefügt haben, machen die Methoden in diesen Klassen im Hinblick auf den funktionalen Zusammenhalt der Klassen keinen Sinn mehr. Sie haben zum Beispiel eine FileNode
die eine Methode hat calculateFileColorForFunctionABC()
um die neuesten Visualisierungsfunktionen im Dateisystem zu implementieren.
Das Visitor Pattern (wie viele andere Entwurfsmuster) entstand aus der Schmerzen und Leiden von Entwicklern, die wussten, dass es eine bessere Möglichkeit gab, ihren Code zu ändern, ohne dass überall viele Änderungen erforderlich waren, und die außerdem gute Entwurfsprinzipien (hohe Kohäsion, geringe Kopplung) einhielten. Ich bin der Meinung, dass es schwer ist, den Nutzen vieler Muster zu verstehen, bevor man diesen Schmerz nicht selbst erlebt hat. Den Schmerz zu erklären (wie wir es oben mit den hinzugefügten "usw."-Funktionalitäten versuchen) nimmt Platz in der Erklärung ein und lenkt ab. Aus diesem Grund ist es schwer, Muster zu verstehen.
Visitor ermöglicht es uns, die Funktionalitäten der Datenstruktur zu entkoppeln (z.B., FileSystemNodes
) aus den Datenstrukturen selbst. Das Muster erlaubt es dem Design, Kohäsion zu respektieren - Datenstrukturklassen sind einfacher (sie haben weniger Methoden) und auch die Funktionalitäten sind gekapselt in Visitor
Implementierungen. Dies geschieht über Doppeldisposition (das ist der komplizierte Teil des Musters): mit accept()
Methoden in den Strukturklassen und visitX()
Methoden in den Visitor-Klassen (der Funktionalität):
Diese Struktur ermöglicht es uns, neue Funktionalitäten hinzuzufügen, die auf der Struktur als konkrete Besucher arbeiten (ohne die Strukturklassen zu ändern).
Zum Beispiel, ein PrintNameVisitor
das die Verzeichnislistenfunktionalität implementiert, und eine PrintSizeVisitor
die die Version mit der Größe implementiert. Wir könnten uns vorstellen, eines Tages einen "ExportXMLVisitor" zu haben, der die Daten in XML generiert, oder einen anderen Besucher, der sie in JSON generiert, usw. Wir könnten sogar einen Besucher haben, der meinen Verzeichnisbaum anzeigt, indem er ein grafische Sprache wie DOT , die mit einem anderen Programm visualisiert werden sollen.
Ein letzter Hinweis: Die Komplexität von Visitor mit seinem Double-Dispatch bedeutet, dass es schwieriger zu verstehen, zu programmieren und zu debuggen ist. Kurz gesagt, es hat einen hohen Geek-Faktor und widerspricht dem KISS-Prinzip. In einer von Forschern durchgeführten Umfrage wurde festgestellt, dass das Muster "Visitor" umstritten ist (es gab keinen Konsens über seine Nützlichkeit). Einige Experimente zeigten sogar, dass der Code dadurch nicht leichter zu pflegen ist.