Ist das Visitor-Muster für dynamisch typisierte Sprachen nützlich?

8

Das Visitor-Muster ermöglicht das Schreiben von Operationen auf Objekte, ohne die Objektklasse zu erweitern. Sicher. Aber warum schreibe nicht einfach eine globale Funktion oder eine statische Klasse, die meine Objektsammlung von außen manipuliert? Grundsätzlich wird in einer Sprache wie Java eine accept() Methode aus technischen Gründen benötigt; Aber in einer Sprache, in der ich dasselbe Design ohne eine accept() -Methode implementieren kann, wird das Besuchermuster trivial?

Erläuterung: Im besuchenden Muster haben besuchbare Klassen (Entitäten) eine Methode .accept() , deren Aufgabe es ist, die Methode .visit() des Besuchers für sich selbst aufzurufen. Ich kann die Logik der Java-Beispiele sehen: Der Besucher definiert eine andere .visit(n) -Methode für jeden von ihm unterstützten visitable-Typ n , und der .accept() -Tick muss verwendet werden, um zur Laufzeit zwischen diesen auszuwählen. Aber Sprachen wie Python oder PHP haben dynamische Typisierung und keine Methode überladen. Wenn ich ein Besucher bin, kann ich eine Entitätsmethode (z. B. .serialize() ) aufrufen, ohne den Typ der Entität oder sogar die vollständige Signatur der Methode zu kennen. (Das ist das Problem der doppelten Zustellung, richtig?)

Ich weiß, dass eine Annahme-Methode geschützte Daten an den Besucher weitergeben kann, aber was ist der Sinn? Wenn die Daten den Besucherklassen zugänglich gemacht werden, sind sie effektiv Teil der Klassenschnittstelle, da ihre Details außerhalb der Klasse liegen. Die Offenlegung privater Daten erschien mir nie als der Punkt des Besuchermusters.

So scheint es, dass ich in Python, Ruby oder PHP eine Besucher-ähnliche Klasse ohne Accept-Methode im besuchten Objekt (und ohne Reflektion) implementieren kann, oder? Wenn ich mit einer Familie heterogener Objekte arbeiten kann und ihre öffentlichen Methoden ohne Kooperation aus der Klasse "Visited" aufrufen kann, verdient dies noch das "Besuchermuster"? Gibt es etwas an der Essenz des Musters, das ich vermisse, oder läuft es nur darauf hinaus, "eine neue Klasse zu schreiben, die deine Objekte von außen manipuliert, um eine Operation auszuführen"?

PS. Ich habe mir viele Diskussionen über SO und anderswo angeschaut, konnte aber nichts finden, was diese Frage angeht. Zeiger willkommen.

    
alexis 22.06.2012, 10:43
quelle

6 Antworten

1

Diese Antwort wird mit einer Unkenntnis von PHP usw. gemacht, aber der Besucher muss typischerweise mehr als nur eine einzige Methode (Sie erwähnten "serialisieren") auf den Entitäten aufrufen. Wenn die Visit () -Methode für den konkreten Besucher aufgerufen wird, kann der Besucher für jeden Entitätsuntertyp einen anderen Code ausführen. Ich sehe nicht, wie sich das von einer dynamisch-typisierten Sprache unterscheidet (obwohl ich etwas Feedback mögen würde).

Ein weiterer netter Vorteil von Visitor ist die saubere Trennung des Codes, der für jede Entität aus dem Code ausgeführt wird, der die Entitäten auflistet. Dies hat mir in mindestens einem großen Projekt einige schwerwiegende Code-Duplikationen erspart.

Abgesehen davon habe ich Visitor in Sprachen verwendet, in denen die Methode nicht überladen wurde. Sie ersetzen nur Visit (TypeN n) durch VisitN (TypeN n).

Folge von Kommentaren.

Das ist ein Besucher-Pseudo-Code, und ich weiß nicht, wie ich es so ohne die Kooperation des besuchten Objektes (zumindest ohne einen Switch-Block) machen würde:

%Vor%

Die Besucher-Infrastruktur ermöglicht die Verarbeitung einer großen Anzahl von Befehls-Subtypen ohne Select Case, andernfalls swithc.

In Bezug auf den Besucher, der sich mit der Aufzählung beschäftigt, denke ich, dass Sie sich so beschränken. Das heißt nicht, dass eine kooperierende Klasse (ein abstrakter VisitorEnumerator) nicht beteiligt sein kann.

Beachten Sie beispielsweise, dass dieser Besucher die Reihenfolge der Aufzählung nicht kennt:

%Vor%

Und so kann es wie folgt wiederverwendet werden:

%Vor%

und das Aufzählen des umgekehrten Weges mit demselben Besucher:

%Vor%

In echtem Code würde ich eine Basisklasse für den Enumerator erstellen und dann eine Unterklasse für die verschiedenen Enumerationsszenarien erstellen, während ich die konkrete Visitor-Unterklasse übergeben würde, um sie vollständig zu entkoppeln. Hoffentlich können Sie die Macht sehen, die Aufzählung getrennt zu halten.

    
tcarvin 22.06.2012, 13:24
quelle
2

Der Ort, an dem Visitor besonders nützlich ist, ist, wo der Visitor den Typ von Visitees einschalten muss, und aus welchem ​​Grund auch immer, wollen Sie dieses Wissen nicht in die Visitees (Think-Plugin-Architekturen) codieren. Betrachten Sie den folgenden Python-Code:

Besucherstil

%Vor%

(Beachten Sie, dass wir die Visitee-Logik mit einer Basisklasse / Mixin komprimieren könnten).

Vergleiche mit:

Nicht-Besucher-Stil

%Vor%

Im zweiten Beispiel benötigen die Früchte keine spezielle Unterstützung für den "Besucher" und der "Besucher" behandelt die Abwesenheit von Logik für den gegebenen Typ.

Im Gegensatz dazu ist das zweite Beispiel in Java oder C ++ nicht wirklich möglich, und die Visit-Methode (in den Visitees) kann einen Namen verwenden, um auf alle Versionen der Prozessmethode zu verweisen; Der Compiler wählt die Version aus, die für den übergebenen Typ gilt. und der Besucher kann leicht eine Standardimplementierung für die Stammklasse für den Typ der Besucher bereitstellen. Es ist auch notwendig, eine Besuchsmethode in den Besuchern zu haben, da die Methodenvariante (z. B. process(Banana b) vs process(Apple a) ) zur Kompilierzeit im Code ausgewählt wird, der für die Methode visit des Besuchers generiert wird.

Dementsprechend gibt es in Sprachen wie Python oder Ruby, wo es keine Parametertypen gibt (oder vielmehr muss der Programmierer sie selbst implementieren), das Besuchermuster nicht nötig. Alternativ könnte man sagen, dass das Besucher-Muster besser implementiert wird, ohne dass es durch die Methoden des Besuchs geschickt wird.

Im Allgemeinen ist es in dynamischen Sprachen wie Python, Ruby oder Smalltalk besser, wenn die "visitee" -Klassen alle erforderlichen Informationen (hier das zutreffende Verb) enthalten und, wenn nötig, Hooks zur Unterstützung des "Besuchers" bereitstellen B. Befehls- oder Strategie-Pattern oder verwenden Sie das hier gezeigte Nicht-Besucher-Muster.

Fazit

Der Nicht-Besucher ist eine saubere Möglichkeit, die Typumschaltungslogik zu implementieren, obwohl die explizite Typumschaltung normalerweise ein Code-Geruch ist. Denken Sie daran, dass die Java- und C ++ - Methode dies auch explizit im Besucher ausführt; Die Eleganz des Musters in diesen Sprachen besteht darin, dass es keine explizite Umschaltlogik in den Visitees gibt, was in dynamischen Sprachen mit nicht typisierten Variablen nicht möglich ist. Dementsprechend ist das Besuchermuster oben für dynamische Sprachen schlecht, weil es die Sünde reproduziert, die das Besuchermuster in statischen Sprachen vermeiden will.

Die Sache mit der Verwendung von Mustern ist, dass Sie, anstatt UML-Diagramme sklavisch zu reproduzieren, verstehen müssen, was sie erreichen wollen und wie sie diese Ziele mit der konkret betrachteten Sprachmaschinerie erreichen. In diesem Fall sieht das Muster zum Erzielen derselben Vorzüge anders aus und weist ein anderes Anrufmuster auf. Dadurch können Sie sie an verschiedene Sprachen anpassen, aber auch an verschiedene konkrete Situationen innerhalb derselben Sprache.

Update: Hier ist ein Ruby-Artikel zur Implementierung dieses Musters: Ссылка

Der doppelte Versand scheint mir ziemlich aufgezwungen zu sein; Du könntest es, soweit ich das beurteilen kann, einfach abschaffen.

    
Marcin 17.04.2015 10:48
quelle
0

Ich denke, dass Sie Visitor Pattern und Double Dispatch synonym verwenden. Wenn Sie sagen,

Wenn ich mit einer Familie heterogener Objekte arbeiten und ihre öffentlichen Methoden ohne Kooperation aus der Klasse "visited" aufrufen kann, verdient das dann noch das "Besuchermuster"?

und

Schreiben Sie eine neue Klasse, die Ihre Objekte von außen manipuliert, um eine Operation auszuführen "?

Sie definieren, was Double-Versand ist. Sicher, Besuchermuster wird durch doppelten Versand implementiert. Aber es gibt etwas mehr zu dem Muster selbst.

  • Jeder Besucher ist ein Algorithmus über eine Gruppe von Elementen (Entitäten) und neue Besucher können eingesteckt werden, ohne den bestehenden Code zu ändern. Open / Closed Prinzip.
  • Wenn neue Elemente häufig hinzugefügt werden, sollte das Besuchermuster am besten vermieden werden
user1168577 22.06.2012 12:06
quelle
0

Vielleicht hängt es von der Sprache ab.

Das Besuchermuster löst Probleme mit doppelter und mehrfacher Hierarchie in Sprachen, die nicht Mehrfach-Versand enthalten. Nimm Ruby, Lisp und Python. Sie sind alle dynamisch typisierte Sprachen, aber nur CLOS-Lisp implementiert Mehrfachversand im Standard. Dies wird auch als Multimethods bezeichnet und Python und Ruby können es anscheinend implementieren, indem sie Erweiterungen verwenden.

Ich mag diesen seltsamen Kommentar auf wikipedia , in dem steht:

  

Lisp's Objektsystem [CLOS] mit seiner Mehrfachversendung ersetzt nicht das Besuchermuster,   sondern bietet lediglich eine prägnantere Umsetzung davon, in der das Muster alles andere als   verschwindet.

In anderen Sprachen, auch in statisch getippten, müssen Sie die Abwesenheit von Multimethoden umgehen. Das Besuchermuster ist ein solcher Weg.

    
Joao Tavora 22.06.2012 11:19
quelle
0

Besuchermuster für mich bedeutet, dass Objekte basierend auf ihrem Typ neue Funktionen hinzugefügt werden. Anscheinend hab 'ich / sonst Leitern, typspezifische Operationen durchzuführen, ist schlecht (Ich hätte gerne eine Erklärung dafür :(). Bei Python konnte ich das ohne das ganze Doppel-Dispatch-Drama machen, indem monkeypatching (eine andere schlechte Idee) bestimmte Funktionen als Klassenmethoden.

Ich fragte nach hier .

Im folgenden Beispiel wird davon ausgegangen, dass eine Basisklasse ASTNode und eine große Klassenhierarchie darunter liegen ( ASTVar , ASTModule , ASTIf , ASTConst usw.). Diese Klassen haben nur ihre spezifischen Datenattribute und trivialen Methoden.

Angenommen, der Klassencode ist gesperrt (oder die Funktionalität ist von den Daten getrennt). Jetzt habe ich Methoden, die Klassen dynamisch zugewiesen sind. Beachten Sie, dass im folgenden Beispiel der Aufrufname der Iterations- / Rekursionsmethode (stringify) vom Funktionsnamen ( nodeType _stringify) abweicht.

%Vor%

Ich kann die Klassen (möglicherweise einmalig während der Modulinitialisierung) mit Funktionalität erweitern, wann immer ich will (schlechte Idee?).

%Vor%

Nun würde der Aufruf von my_root_node.stringify() die richtigen untergeordneten Methoden (rekursiv) aufrufen, ohne explizit nach Typ zu suchen.

Ist diese Technik nicht ähnlich wie das Hinzufügen von Methoden zu den JavaScript-Prototypen ( Besuchermuster in JS )? ).

War das nicht das Ziel von Visitor Pattern? Erweiterung von code-locked Typen? Sicher, die Notwendigkeit, double-dispatch zu verwenden ( VisitorObject.visit(ConcreteObject) wird von ConcreteObject.Accept(VisitorObject) aufgerufen) wäre in Python, das dynamisch typisiert wird, nicht notwendig. Wahrscheinlich wird jemand dies für dynamisch typisierte Sprachen formalisieren, und wir werden ein neues Muster zur Hand haben oder nicht. Schließlich werden Muster entdeckt, nicht erfunden (ich kann mich nicht erinnern, wo ich das gelesen habe).

    
Sonal Pinto 01.10.2016 02:01
quelle
0

Besuchermuster macht 2 Dinge:

  • Erlaubt Ad-hoc-Polymorphie (gleiche Funktion, aber verschiedene Dinge zu verschiedenen "Typen").
  • Ermöglicht das Hinzufügen eines neuen verbrauchenden Algorithmus, ohne den Datenanbieter zu ändern.

Sie können in dynamischen Sprachen Sekunden ohne Informationen zum Typ Visitor oder Laufzeit ausführen. Aber zuerst benötigt man einen expliziten Mechanismus oder ein Entwurfsmuster wie Visitor.

    
przemo_li 23.11.2017 06:38
quelle