Visitor mit Multi-Dispatch (nicht nur Double-Dispatch)

18/01/2014 - 12:13 von Heiner Kücker | Report spam
Hallo Leutz,

ich wurde letztens mal nach dem Visitor-Pattern gefragt, naja, es war ein Projektbewerbungsgespràch, wann wird man sonst schon mal nach so was gefragt.

Das GoF-Buch habe ich vor Jahren zuletzt gelesen und eigentlich habe ich das Visitor-Pattern für mich als unbenutzbar abgehakt.

Dann habe ich aber doch mal auf Wikipedia nachgelesen und dachte, für meine Expressions im Constraint-Code-Generator könnte ich das schon mal benutzen.

Auf der Wikipedia-Seite steht, dass es ein Workaround für den Single-Dispatch in Java(oder anderen solchen Sprachen) ist.

Das Visitor-Pattern realisiert einen Double-Dispatch, Multi-Dispatch ist nicht möglich.

Nun ist es so eine Art Ràtsel oder Herausforderung, wie man mit dem Visitor-Pattern einen Multi-Dispatch hinbekommt.

instanceof oder so ist verboten.

Geschmeidig finde ich am Visitor, dass man nur an einer Stelle aufpassen muss, beim Implementieren der visit-Methode in der besuchbaren Klasse die richtige Methode des Visitor-Interface aufruft, alles andere kontroliiert dann der Compiler, zumindest in Java.

Ich habe es hinbekommen und ein Beispiel gibt es auf

http://heinerkuecker.de/VISITOR_MULTIDISPATCH.html

Die Grundidee ist, bei mehreren zu dispatchenden Parametern, für den jeweiligen Parameter einen weiteren Vistor zu erzeugen, der aus den möglichen Methoden (kartesisches Produkt der konkreten Parameter-Klassen) die für den aktuellen Parameter passenden Methoden auswàhlt.

Am Ende wird der letzte Visitor ganz normal aufgerufen.

In den spezialisierten Visitoren wird der jeweilige Parameter als Member über den Konstruktor vermerkt.

Das Konstrukt ist ein wenig rekursiv, àhnlich zum funktionalen Pattern-Matching, aber jede Ebene ist spezifisch und die Anzahl Ebenen begrenzt.

Manuell ist das eine Menge Schreibarbeit.
Das Fehler-Risiko ist allerdings begrenzt, weil der Compiler immer mit-kontrolliert.

Also würde sich ein Code-Generator dafür anbieten.

Diese Arbeit habe ich erst mal gescheut, bei Interesse von Benutzerseite und eventuell noch vorhandener Zeit, würde ich das aber mal angehen.


Schöne Grüße
Heiner Kücker
 

Lesen sie die antworten

#1 Patrick Roemer
18/01/2014 - 17:04 | Warnen spam
Responding to Heiner Kücker:

ich wurde letztens mal nach dem Visitor-Pattern gefragt, naja, es
war ein Projektbewerbungsgespràch, wann wird man sonst schon mal
nach so was gefragt.

Das GoF-Buch habe ich vor Jahren zuletzt gelesen und eigentlich habe
ich das Visitor-Pattern für mich als unbenutzbar abgehakt.



Dann hast Du wahrscheinlich nicht viel mit Parsern,
Bytecode-Modifikation, o.ae. gemacht - sobald AST-artige Strukturen im
Spiel sind, sind Visitor-basierte APIs eigentlich allgegenwaertig (und
IMO auch das beste, was man in Java in diesen Faellen machen kann).

Nun ist es so eine Art Ràtsel oder Herausforderung, wie man mit dem
Visitor-Pattern einen Multi-Dispatch hinbekommt.



...was auch gar nicht so eine exotische Anforderung ist: Die Frage
stellt sich schnell, wenn man z.B. einen Knoten fuer einen binaeren
Operator hat und die beiden (polymorphen) Operanden in Beziehung setzen
will.

Ich habe es hinbekommen und ein Beispiel gibt es auf

http://heinerkuecker.de/VISITOR_MULTIDISPATCH.html



Ich halte es ja fuer eine Unsitte, hier eher abstrakte Plaudereien
vorzustellen und die mit einem Link auf eine Webseite zu garnieren, die
auch wieder wenig konkretes enthaelt ausser einem Link auf ein Zip.
Viele Mitdiskutanten wirst Du hier wie anderswo so kaum finden. Aber
sei's drum...

An Deiner Implementierung finde ich unschoen, dass der Multi-Dispatch in
die Domaenenklassen hereinleakt:

public interface AMultiVisitable {
public BVisitor acceptMulti( AVisitor visitor);
}

Ich wuerde fuer A und B symmetrisch "normale" Visitor/Visitable-Paare
deklarieren - das reicht echt an kognitivem Overload. Den Multi-Dispatch
kann man dann darauf basierend implementieren:

// ABVisitor ~ Dein ASwitchVisitor
public final class ABVisitor implements AVisitor {
// ABDispatcher ~ Dein ABMultiVisitor (ist schliesslich kein Visitor)
private final ABDispatcher dispatcher;
private final BVisitable b;

private ABVisitor(ABDispatcher dispatcher, BVisitable b) {
this.dispatcher = dispatcher;
this.b = b;
}

// Client-Aufruf
public static void dispatch(
AVisitable a, BVisitable b, ABDispatcher disp) {
a.accept(new ABVisitor(disp, b));
}

@Override
public void visitA1(A1 a1 ) {
b.accept(new A1BVisitor(a1, dispatcher));
}

@Override
public void visitA2(final A2 a2 ) {
b.accept(new A2BVisitor(a2, dispatcher));
}
}

Das Konstrukt ist ein wenig rekursiv, àhnlich zum funktionalen
Pattern-Matching, [...]



Beim Pattern Matching braucht man ja gerade nicht unbedingt Nesting,
sondern man kann das auch "flach" erledigen. In Scala:

def dispatch(a: A, b: B) (a, b) match {
case (a1: A1, b1: B1) => println("A1-B1")
case (a1: A1, b2: B2) => println("A1-B2")
case (a2: A2, b1: B1) => println("A2-B1")
case (a2: A2, b2: B2) => println("A2-B2")
}

Es gibt uebrigens auch (OO-)Sprachen, die Multimethoden unterstuetzen,
etwa CLOS oder Nice.

Viele Gruesse,
Patrick

Ähnliche fragen