C ++: Zweifel am Besuchermuster

8

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?

    
peoro 14.11.2010, 12:20
quelle

5 Antworten

13

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:

  • Wenn die Anzahl der Klassen sich schneller ändert als die Anzahl der Operationen, verwendet virtuelle Funktionen . Der Grund dafür ist, dass zum Hinzufügen einer Klasse alle Besucher geändert werden müssen.
  • Wenn die Anzahl der Klassen im Vergleich zur Anzahl der Operationen relativ stabil ist , verwenden Besucher . Dies liegt daran, dass das Hinzufügen einer virtuellen Funktion das Ändern aller Klassen in der Hierarchie erfordert.

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 .

    
sbi 14.11.2010, 13:25
quelle
0

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.

    
Martin Broadhurst 14.11.2010 12:40
quelle
0

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.

    
djna 14.11.2010 12:41
quelle
0

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 :

darstellt %Vor%     
Paul Michalik 14.11.2010 12:55
quelle
0

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

    
Richard Gomes 29.01.2011 23:30
quelle