Eleganteste Art, dieses Polymorphie-Problem zu umgehen

8

EDIT: Ich arbeite mit C ++.

Also erstelle ich Methoden / Funktionen, um die Schnittmenge zwischen Formen zu testen. Ich habe im Wesentlichen folgendes:

%Vor%

Nun muss ich mich für den besten Weg entscheiden, die tatsächlichen Methoden / Funktionen zu schreiben, um die Kreuzung zu testen. Aber alle meine Shapes werden in einer Liste von Shape Zeigern gespeichert, also werde ich eine Methode / Funktion des Basisformulars aufrufen:

%Vor%

An diesem Punkt muss ich bestimmen, welche Arten von Formen "a" und "b" sind, damit ich Kollisionen richtig erkennen kann. Ich kann leicht einen von ihnen machen, indem ich einfach einige virtuelle Methoden benutze:

%Vor%

Das würde eine der Formen bestimmen ('a' ist jetzt 'das'). Allerdings müsste ich immer noch die Art von "b" bekommen. Die naheliegende Lösung besteht darin, Shape eine ID-Variable zu geben, um zu klassifizieren, welche Form es ist, und dann durch diese zu wechseln und dann dynamic_cast zu verwenden. Allerdings ist das nicht sehr elegant, und es fühlt sich an, als ob es einen OO Weg geben sollte, dies zu tun.

Irgendwelche Vorschläge?

    
Brandon oubiub 26.01.2012, 06:01
quelle

4 Antworten

7

Wie @Mandarse sagte, handelt es sich um ein typisches Doppelversandproblem. In objektorientierten Sprachen oder in C ++ - Sprachen, die objektorientierte Konzepte implementieren können, wird dies normalerweise mithilfe des Visitor -Musters

Die Schnittstelle Visitor selbst definiert im Allgemeinen einen Callback pro konkreten Typ.

%Vor%

Dann ist die Hierarchie Shape dafür angepasst. Wir brauchen zwei Methoden: die eine, um jede Art von Besucher zu akzeptieren, die andere, um den "geeigneten" Besucher der Kreuzung zu erstellen.

%Vor%

Der Intersecter ist einfach:

%Vor%

Zum Beispiel gibt es für Circle:

%Vor%

Und die Verwendung:

%Vor%

Wenn andere Methoden einen Double-Dispatch-Mechanismus benötigen, müssen Sie lediglich eine andere "Intersecter-like" -Klasse erstellen, die das Ergebnis umschließt und von Visitor erbt, und eine neue "Factory" -Methode mit Shape Dies wird von abgeleiteten Klassen überschrieben, um die entsprechende Operation bereitzustellen. Es ist ein bisschen langatmig, funktioniert aber.

Hinweis: Es ist sinnvoll, dass intersect(circle, rectangle) und intersect(rectangle, circle) das gleiche Ergebnis liefern. Sie können den Code mit einigen Methoden faktorisieren und haben CircleIntersecter::visit delegates an der konkreten Implementierung. Dies vermeidet Code-Duplikation.

    
Matthieu M. 26.01.2012, 07:47
quelle
4

Andrei Alexandrescu hat dieses Problem in seinem klassischen modernen C ++ Design ausführlich beschrieben. Die Begleitbibliothek Loki enthält die die Implementierung für Multi-Methoden .

Aktualisieren

Loki bietet drei Implementierungen von Multi-Methoden, abhängig von den Bedürfnissen des Benutzers. Einige sind der Einfachheit halber, einige sind für die Geschwindigkeit, einige sind gut für niedrige Kopplung und einige bieten mehr Sicherheit als andere. Das Kapitel in diesem Buch umfasst fast 40 Seiten und geht davon aus, dass der Leser mit vielen der Konzepte des Buches vertraut ist - wenn Sie sich mit Boost auskennen, dann ist Loki vielleicht in Ihrer Nähe. Ich kann das wirklich nicht zu einer für SO akzeptablen Antwort destillieren, aber ich habe Sie auf die beste Erklärung des Themas für C ++ hingewiesen, die ich kenne.

    
justin 26.01.2012 06:23
quelle
2

Der C ++ - Laufzeitpolymorphismus hat einen einzigen Dispatch (die Basisklasse vtable).

Es gibt verschiedene Lösungen für Ihr Problem, aber keine von ihnen ist "elegant", da sie alle versuchen, die Sprache zu zwingen, mehr zu tun, was sie nativ unterstützen kann (Alexandrescu Loki multimethods ist ein sehr gut versteckter Satz von Hacks: es kapselt die "schlechten dinge", macht aber dann nicht gut)

Das Konzept, hier, dass Sie alle N 2 Funktionen der möglichen Kombinationen schreiben müssen und eine Möglichkeit finden, sie auf der Grundlage des tatsächlichen Laufzeittyps von TWO-Parametern aufzurufen. Das "Besuchermuster" (Aufruf einer virtuellen Funktion von einer anderen virtuellen Funktion), die "Mutimethod" -Technik (Verwendung einer generischen dspatch-Tabelle), die "dynamische Besetzung" in eine virtuelle Funktion oder die "dual dynamic_cast" aus allen Funktionen heraus alle machen das Gleiche: Rufen Sie eine Funktion nach zwei Umleitungen auf. Keiner von ihnen kann technisch "besser als der andere" definiert werden, da die resultierende Leistung meist die gleiche ist.

Aber einige von ihnen kosten mehr als die anderen beim Code-Schreiben und andere kosten mehr in der Code-Pflege. Sie werden höchstwahrscheinlich versuchen, in Ihrem Fall abzuschätzen, was der Kompromiss ist. Wie viele andere Klassen denken Sie, dass in Zukunft möglicherweise hinzugefügt werden muss?

    
Emilio Garavaglia 26.01.2012 07:57
quelle
1

Sie können jedem shapeType

ein Feld Shape hinzufügen

Zum Beispiel:

%Vor%     
Jakub M. 26.01.2012 06:38
quelle

Tags und Links