What is the difference between Double Dispatch and the Visitor Pattern?
In short
they come from to different conceptualizations that, in some languages where double dispatch is not natively supported, lead to the visitor pattern as a way to concatenate two (or more) single dispatch in order to have a multi-dispatch surrogate.
In long
The idea of multiple dispatch is - essentially - allow a call like
void fn(virtual base_a*, virtual base_b*);
(note: not as a class member: this is NOT C++! )
that can be overridden as
void fn(virtual derived_a1*, virtual derived_b1*);
void fn(virtual derived_a2*, virtual derived_b1*);
void fn(virtual derived_a1*, virtual derived_b2*);
void fn(virtual derived_a2*, virtual derived_b2*);
so that, when calling
fn(pa, pb)
the call is redirected to the override that matches the actual runtime type of both pa
and pb
. (You can generalize this to whatever number of parameters)
In language like C++, C#, Java, this mechanism does not exist and runtime type dispatching basically works with just one parameter (that, being just one, is made implicit in the function by making the function itself member of the class:
in other words, the pseudocode
void fn(virtual base_a*, base_b*)
becomes the (real C++)
class base_a
{
public:
virtual void fn(base_b*);
}
Note that here there is no more virtual
in front of base_b
, that from now is static.
A call like
pa->fn(pb)
if pa points to a derived_a2 and pb to a derived_b1 will be dispatched to
derived_a2::fn(base_b*), no matter if there is a derived_a2::fn(derived_b1*) in there: the run-time type of the object pointed by pb is not taken into account.
The idea of the visitor patter is that you call the virtual dispatch of an object that calls (eventually back) the virtual dispatch of another:
class base_a
{
public:
virtual void fn(base_b*)=0;
virtual void on_visit(derived_b1*)=0;
virtual void on_visit(derived_b2*)=0;
};
class base_b
{
public:
virtual void on_call(derived_a1*)=0;
virtual void on_call(derived_a2*)=0;
};
//forward declarations, to allow pointers free use in other decls.
class derived_a1;
class derived_b1;
class derived_a1: public base_a
{
public:
virtual void fn(base_b* pb) { pb->on_call(this); }
virtual void on_visit(derived_b1* p1) { /* useful stuff */ }
...
};
class derived_b1: public base_b
{
public:
virtual void on_call(derived_a1* pa1) { pa1->on_visit(this); }
...
};
now, a call like pa->fn(pb)
, if pa points to derived_a1 and pb to derived_b1, will finally go to derived_a1::on_visit(derived_b1*)
.
Visitor pattern is one solution which implements the behaviour of double dispatch. There can be several other solutions as well. The term double dispatch itself doesn't give any idea of solution, in fact it is a problem whose solution is provided by visitor pattern.
In C# (4.0), one could use dynamic
keyword to implement double dispatch, in which case visitor pattern is not required. Here is my solution to double-dispatch problem using dynamic
keyword:
Dynamic Dispatch refers to the concept of dispatching to a method based on runtime information, in general. Most OO systems (as in Java/C#/C++) usually implement dynamic dispatch via virtual
methods (whether or not all methods are virtual depend from the language); this restricts them to dispatch according to a single method argument (the implicit object reference).
In general you could want to dispatch according to an arbitrary number of elements. Double Dispatch, for example, is the requirement/ability to dispatch according to two arguments of the method.
On the other hand, the Visitor Pattern is an implementation of Multi Dispatch in general and thus Double Dispatch in particular in such OO systems.
Double dispatch is a technical problem, which can, depending on the language, be solved in different manners—some languages support double dispatch directly. The visitor pattern is a pattern which can be used to solve different problems. In the case of C++, it's the most frequent (but not the only) solution used for double dispatch, but it's not used exclusively for that, and it can be useful even in languages that do support double dispatch.
© 2022 - 2024 — McMap. All rights reserved.