Ich weiß, was Besuchermuster ist und wie man es benutzt; Diese Frage ist kein Duplikat dieses one .
Ich habe eine Bibliothek, in die ich den Großteil des wiederverwendbaren Codes, den ich schreibe, stecke und den ich mit den meisten meiner Projekte verknüpfe.
Oft muss ich einigen Klassen Features hinzufügen, ohne diese neuen Funktionen der Bibliothek hinzuzufügen. Lassen Sie mich ein reales Beispiel verwenden:
In dieser Bibliothek habe ich eine Klasse Shape
, geerbt von CircleShape
, PolygonShape
und CompositeShape
.
Ich entwickle jetzt eine grafische Anwendung, wo ich diese Shape
rendern muss, aber ich möchte keine virtuelle Funktion render
in die Core Shape
Klasse einfügen, da einige meiner Projekte% verwenden co_de% macht kein Rendering, und andere grafische Projekte könnten unterschiedliche Rendering-Engines verwenden (ich verwende Qt für dieses Projekt, aber für ein Spiel würde ich OpenGL verwenden, daher benötigt die Shape
-Funktion andere Implementierungen).
Der berühmteste Weg, dies zu tun, ist natürlich das Besuchermuster, aber das wirft ein paar Zweifel auf:
Jede Klasse einer Bibliothek könnte erweitert werden müssen, wie meine render
tut. Die meisten öffentlichen Bibliotheken (über alle) bieten keine Unterstützung für Besuchermuster; Warum? Warum sollte ich?
Besucher Muster ist eine Möglichkeit, Double Dispatching in C ++ zu simulieren. Es ist in C ++ nicht nativ und muss explizit implementiert werden, was die Klassenschnittstelle komplexer macht: Ich denke nicht, dass die Funktion Shape
auf der gleichen Ebene der Funktionen meiner Klasse sein sollte, ich sehe das wie die Abstraktion zu brechen / p>
Das explizite Hochladen von applyVisitor
mit Shape
ist teurer, aber für mich sieht es nach einer saubereren Lösung aus.
Also, was soll ich tun? Double Dispatching in allen meinen Bibliotheksklassen implementieren? Was, wenn die Bibliothek, die dynamic_cast
zur Verfügung stellt, nicht meine, sondern irgendeine GPL-Bibliothek im Internet gefunden hat?
Erstens: "Besuchermuster ist eine Möglichkeit, Double Dispatching in C ++ zu simulieren." Das ist, äh, nicht ganz richtig. Eigentlich ist double dispatch eine Form der multiples dispatch, die eine Möglichkeit ist, (die fehlenden) Multi-Methoden in C ++ zu simulieren.
Ob Operationen auf einer Klassenhierarchie durch das Hinzufügen virtueller Funktionen oder durch das Hinzufügen von Besuchern implementiert werden sollen durch die Wahrscheinlichkeiten des Hinzufügens von Klassen gegen das Hinzufügen von Operationen:
Ja, viele Bibliotheken haben keine Besucherschnittstelle.
Wenn wir nur die obigen Überlegungen betrachten, wäre das richtig, wenn sich die Anzahl der Klassen oft ändert. Das heißt, wenn eine Bibliothek häufig veröffentlicht wird und ständig neue Klassen hinzugefügt werden, wäre die Bereitstellung einer Besucherschnittstelle wenig sinnvoll, da jedes Mal, wenn eine neue Version neue Klassen enthält, alle Benutzer ihre Besucher anpassen müssen . Wenn wir uns nur die obigen Überlegungen ansehen, scheint eine Besucherschnittstelle nur dann hilfreich zu sein, wenn sich die Anzahl der Klassen in der Klassenhierarchie einer Bibliothek selten oder nie ändert.
Bei Bibliotheken von Drittanbietern gibt es jedoch einen anderen Aspekt: Normalerweise können Benutzer die Klassen in der Bibliothek nicht ändern. Das heißt, wenn sie eine Operation hinzufügen müssen, können sie dies nur tun, indem sie einen Besucher hinzufügen - , wenn die Bibliothek die Hooks bereitstellt damit sie hineinstecken können .
Wenn Sie also eine Bibliothek schreiben und das Gefühl haben, dass Benutzer in der Lage sein sollten, Operationen hinzuzufügen, müssen Sie eine Möglichkeit bereitstellen, ihre Besucher in Ihre lib einzubinden .
Das sieht für mich nicht nach dem Besuchermuster aus.
Ich würde vorschlagen, dass Sie eine Klasse RenderableShape
haben, die ein Objekt Shape
aggregiert und dann Unterklassen für jede Form erstellt. RenderableShape
hätte eine virtuelle render
-Methode.
Wenn Sie mehrere Rendering-Engines unterstützen möchten, können Sie eine Basisklasse " RenderContext
" erstellen, die Zeichenoperationen abstrahiert, mit Unterklassen für jede Rendering-Engine, wobei jede Unterklasse die Zeichenoperationen in Bezug auf die Rendering-Engine implementiert. Sie haben dann RenderableShape::render
nehmen ein RenderContext
als Argument, und zeichnen Sie es mit seiner abstrahierten API.
Also gibt es eine Klasse xxxShape, die in gewisser Weise Informationen enthält, die das Rendering "antreiben". Für Kreise, die Mittelpunkt, Radius, für Quadrate einige Eckkoordinaten oder ähnliches sein könnten. Vielleicht ein paar andere Sachen über Füllungen und Farben.
Sie wollen / können diese Klassen nicht aktualisieren, um die tatsächliche Rendering-Logik hinzuzufügen, und ich denke, Ihre Gründe dafür sind gültig / unvermeidlich.
Aber vermutlich haben Sie genug öffentliche Zugriffsmethoden auf die Klassen, damit Sie die "treibenden" Informationen bekommen können, sonst sind Sie verloren.
In diesem Fall können Sie diese Elemente nicht einfach umbrechen:
%Vor%und so weiter. Verwenden Sie nun das Besuchermuster in den Renderer-Klassen.
Es gibt viele mögliche Lösungen, aber Sie könnten dies tun, zum Beispiel: Starten Sie eine neue Hierarchie, die Shapes
in einem bestimmten Context
:
Ich verstehe absolut, was Sie gesagt haben, und ich teile die gleichen Bedenken. Das Problem ist, dass das Besuchermuster nicht sehr klar definiert ist und die ursprüngliche Lösung dafür irreführend ist, IMHO. Deshalb gibt es so viele Variationen dieses Musters.
Insbesondere glaube ich, dass die korrekte Implementierung Legacy-Code unterstützen sollte, ich meine: eine Binärdatei, die Sie den Quellcode überhaupt verloren haben, nicht wahr? Das ist, was die Definition sagt: dass Sie nie die ursprünglichen Datenstrukturen ändern mussten.
Ich mag keine Implementierungen mit visitA, visitB, visitWhatever, acceptA, acceptB, acceptWhatever. Das ist absolut falsch, IMHO.
Wenn Sie eine Chance haben, sehen Sie sich bitte eines Artikels I 'an. Habe darüber geschrieben .
Es ist Java, aber Sie können leicht nach C ++ portieren, wenn Sie es für Ihre Zwecke nützlich finden.
Ich hoffe, es hilft
Prost
Tags und Links c++ visitor-pattern double-dispatch