Ich habe viel über das Besuchermuster und seine angeblichen Vorteile gelesen. Für mich jedoch scheint es, dass sie in der Praxis nicht so viele Vorteile haben:
Es scheint nur eine Menge Arbeit zu sein, wenn Sie nur das wollen:
%Vor%Der einzige wirkliche Vorteil, den ich sehe (den ich übrigens nirgends erwähnt habe): Das Besuchermuster ist wahrscheinlich die schnellste Methode, um den obigen Codeausschnitt in Bezug auf die CPU-Zeit zu implementieren (wenn Sie keine Sprache mit haben doppelter Versand oder effizienter Typvergleich in der Art des Pseudocodes oben).
Fragen:
Aus Erfahrung würde ich sagen, dass "das Hinzufügen eines neuen Typs zur Typhierarchie Änderungen an allen Besuchern erfordert" von Vorteil ist. Weil es dich definitiv zwingt, den neuen Typ zu berücksichtigen, der an ALLEN Stellen hinzugefügt wurde, an denen du typenspezifische Sachen gemacht hast. Es verhindert, dass du einen vergisst ....
Nach meiner persönlichen Meinung ist das Besuchermuster nur dann nützlich, wenn die von Ihnen implementierte Schnittstelle eher statisch ist und sich nicht viel ändert, während Sie jedem die Möglichkeit geben möchten, seine eigene Funktionalität zu implementieren.
Beachten Sie, dass Sie jedes Mal, wenn Sie eine neue Methode hinzufügen, alles ändern, indem Sie eine neue erstellen, anstatt die alte zu ändern - dann müssen Sie nur eine Logik haben, die den Fall behandelt, wenn der Besucher nicht alle Schnittstellen implementiert .
Der Vorteil ist im Grunde, dass Sie die richtige Methode wählen können, die zur Laufzeit statt zur Kompilierungszeit aufgerufen wird - und die verfügbaren Methoden sind tatsächlich erweiterbar.
Weitere Informationen finden Sie in diesem Artikel - Ссылка
Das ist eine alte Frage, aber ich würde gerne antworten.
Das Besuchermuster ist vor allem nützlich, wenn Sie ein zusammengesetztes Muster an der richtigen Stelle haben, in dem Sie einen Baum von Objekten erstellen und eine solche Baumanordnung unvorhersehbar ist.
Typprüfung kann eine Sache sein, die ein Besucher tun kann, aber sagen Sie, dass Sie einen Ausdruck basierend auf einem Baum erstellen möchten, der seine Form entsprechend einer Benutzereingabe oder ähnlichem ändern kann, ein Besucher wäre ein effektiver Weg Sie können den Baum validieren oder ein komplexes Objekt entsprechend den im Baum gefundenen Objekten erstellen.
Der Besucher kann auch ein Objekt tragen, das auf jedem Knoten, den es in diesem Baum findet, etwas tut. Dieser Besucher kann selbst ein Verbund sein, der viele Operationen auf jedem Knoten verkettet, oder er kann ein Vermittlerobjekt tragen, um Operationen zu vermitteln oder Ereignisse auf jedem Knoten zu versenden.
Ihre Phantasie ist die Grenze von all dem. Sie können eine Sammlung filtern, einen abstrakten Syntaxbaum aus einer vollständigen Struktur erstellen, eine Zeichenfolge analysieren, eine Sammlung von Dingen validieren usw.
Soweit ich es bisher gesehen habe, gibt es zwei Nutzungen / Vorteile für das Besucherdesignmuster:
Doppelter Versand
Nehmen wir an, Sie haben eine Fahrzeugklasse und eine Vehiclewasher-Klasse. Der VehicleWasher hat eine Wash (Vehicle) Methode:
%Vor%Zusätzlich haben wir auch spezielle Fahrzeuge wie ein Auto und in Zukunft werden wir auch andere spezifische Fahrzeuge haben. Dafür haben wir eine Car-Klasse, aber auch eine spezifische CarWasher-Klasse, die eine Autowaschanlage hat (Pseudocode):
%Vor%Betrachten Sie dann den folgenden Client-Code, um ein bestimmtes Fahrzeug zu waschen (beachten Sie, dass x und washer mit ihrem Basistyp deklariert sind, da die Instanzen basierend auf Benutzereingaben oder externen Konfigurationswerten dynamisch erstellt werden können; in diesem Beispiel werden sie einfach mit erstellt ein neuer Operator aber):
%Vor%Viele Sprachen verwenden Einzelversand, um die entsprechende Funktion aufzurufen. Single-Dispatch bedeutet, dass zur Laufzeit nur ein einzelner Wert bei der Ermittlung der anzurufenden Methode berücksichtigt wird. Deshalb wird nur die tatsächliche Art der Waschmaschine berücksichtigt. Der tatsächliche Typ von x wird nicht berücksichtigt. Die letzte Codezeile ruft daher CarWasher.Wash (Fahrzeug) und NOT CarWasher.Wash (Auto) auf.
Wenn Sie eine Sprache verwenden, die die Mehrfachversendung nicht unterstützt und Sie diese benötigen (ich kann ehrlich sagen, dass ich noch nie auf eine solche Situation gestoßen bin), können Sie das Besucherdesignmuster verwenden, um dies zu ermöglichen. Dafür müssen zwei Dinge getan werden. Fügen Sie zunächst der Fahrzeugklasse (dem Besucher) eine Accept-Methode hinzu, die einen VehicleWasher als Besucher akzeptiert und dann ihre Operation Wash:
aufruft %Vor%Die zweite Sache ist, den aufrufenden Code zu ändern und die Waschmaschine zu ersetzen.Wash (x); Linie mit den folgenden:
%Vor%Nun wird für den Aufruf der Accept-Methode der tatsächliche Typ von x berücksichtigt (und nur der von x, da angenommen wird, dass wir eine einzige Dispatch-Sprache verwenden). Bei der Implementierung der Accept-Methode wird die Wash-Methode auf dem Washerobjekt (dem Besucher) aufgerufen. Dazu wird der tatsächliche Typ der Waschmaschine berücksichtigt und dies wird CarWasher.Wash (Car) aufrufen. Durch die Kombination von zwei Einzelversendungen wird eine Doppelversendung implementiert.
Nun zu Ihrer Bemerkung die Begriffe wie Akzeptieren und Besuch und Besucher sind sehr unspezifisch. Das ist absolut richtig. Aber es ist aus einem Grund.
Beachten Sie die Anforderung in diesem Beispiel, eine neue Klasse zu implementieren, die Fahrzeuge reparieren kann: ein VehicleRepairer. Diese Klasse kann in diesem Beispiel nur als Besucher verwendet werden, wenn sie von VehicleWasher erbt und ihre Reparaturlogik in einer Wash-Methode enthalten ist. Aber das macht natürlich keinen Sinn und wäre verwirrend. Ich stimme also voll und ganz zu, dass Designmuster dazu neigen, sehr vage und unspezifisch zu benennen, aber sie lassen sie auf viele Situationen anwendbar sein. Je spezifischer Ihre Benennung ist, desto restriktiver kann sie sein.
Ihre switch-Anweisung berücksichtigt nur einen Typ, bei dem es sich tatsächlich um einen manuellen Versandweg handelt. Das Anwenden des Besucherentwurfsmusters in der obigen Weise wird eine doppelte Versendung bereitstellen. Auf diese Weise benötigen Sie beim Hinzufügen zusätzlicher Typen zu Ihrer Hierarchie nicht unbedingt zusätzliche Visit-Methoden. Natürlich fügt es etwas Komplexität hinzu, da es den Code weniger lesbar macht. Aber natürlich haben alle Muster ihren Preis.
Natürlich kann dieses Muster nicht immer verwendet werden. Wenn Sie viele komplexe Operationen mit mehreren Parametern erwarten, ist dies keine gute Option.
Eine Alternative besteht darin, eine Sprache zu verwenden, die mehrere Versandvorgänge unterstützt. Zum Beispiel hat .NET dies bis zur Version 4.0, die das dynamische Keyword eingeführt hat, nicht unterstützt. Dann in C # können Sie Folgendes tun:
%Vor%Da x dann in einen dynamischen Typ konvertiert wird, wird sein tatsächlicher Typ für den Versand berücksichtigt und so werden sowohl x als auch washer verwendet, um die richtige Methode auszuwählen, so dass CarWasher.Wash (Car) aufgerufen wird (macht den Code) richtig funktionieren und intuitiv bleiben.
Getrennte Datenstrukturen und Operationen
Der andere Vorteil und die Anforderung besteht darin, dass die Datenstrukturen von den Vorgängen getrennt werden können. Dies kann ein Vorteil sein, da neue Besucher hinzugefügt werden können, die eigene Operationen haben, während es auch erlaubt, Datenstrukturen hinzuzufügen, die diese Operationen "erben". Es kann jedoch nur angewendet werden, wenn diese Trennung sinnvoll ist. Die Klassen, die die Operationen ausführen (die Besucher), kennen weder die Struktur der Datenstrukturen noch müssen sie etwas wissen, das den Code wartbarer und wiederverwendbar macht. Aus diesem Grund haben die Besucher Operationen für die verschiedenen Elemente in den Datenstrukturen.
Angenommen, Sie haben unterschiedliche Datenstrukturen und sie bestehen alle aus Elementen der Klasse Item.Die Strukturen können Listen, Stapel, Bäume, Warteschlangen usw. sein.
Sie können dann Besucher implementieren, die in diesem Fall die folgende Methode haben:
%Vor%Die Datenstrukturen müssen Besucher akzeptieren und dann die Visit-Methode für jedes Element aufrufen.
Auf diese Weise können Sie alle Arten von Besuchern implementieren, und Sie können immer noch neue Datenstrukturen hinzufügen, solange sie aus Elementen vom Typ Item bestehen.
Für spezifischere Datenstrukturen mit zusätzlichen Elementen (z. B. einem Knoten) könnten Sie einen bestimmten Besucher (NodeVisitor) in Betracht ziehen, der von Ihrem konventionellen Besucher erbt und Ihre neuen Datenstrukturen diesen Besucher akzeptiert (Accept (NodeVisitor)). Die neuen Besucher können für die neuen Datenstrukturen, aber auch für die alten Datenstrukturen aufgrund der Vererbung verwendet werden, und Sie müssen Ihre vorhandene 'Schnittstelle' (in diesem Fall die Superklasse) nicht ändern.
Tags und Links language-agnostic visitor-pattern