Double Dispatch Obsolete?

D

DeMarcus

Since I started with OO I've been told switching on typeid is a big
no-no. E.g.

void Washer::wash( Vehicle myVehicle )
{
if( typeid(myVehicle) == typeid(Car) )
Washer::washCar( myVehicle );
else if( typeid(myVehicle) == typeid(Bike)
Washer::washBike( myVehicle );
else if( typeid(myVehicle) == typeid(Boat)
Washer::washBoat( myVehicle );
}

The alternative is the more correct Double Dispatch. E.g.

void Washer::wash( Vehicle myVehicle )
{
myVehicle.washer( this )
}

void Car::washer( Washer w )
{
w.washCar( this );
}

Now, consider we change Washer to XMLConverter and wash() to write().
This will still work, but when we want to go backwards and read XML and
write a Vehicle we need to switch on some kind of type id label anyway. E.g.

Vehicle XMLConverter::readVehicle( XMLdoc doc )
{
Vehicle v;
string s = doc.readAttr();
if( s == "Car" )
v = new Car();
else if( s == "Bike" )
v = new Bike();
else if( s == "Boat" )
v = new Boat();

return v;
}

So why not just give every MyObject a typeName() method and switch or
std::map<char*, fncPtr> on that throughout all dispatchers?


//Daniel
 
G

Gianni Mariani

DeMarcus said:
Since I started with OO I've been told switching on typeid is a big
no-no. E.g.

void Washer::wash( Vehicle myVehicle )
{
if( typeid(myVehicle) == typeid(Car) )
Washer::washCar( myVehicle );
else if( typeid(myVehicle) == typeid(Bike)
Washer::washBike( myVehicle );
else if( typeid(myVehicle) == typeid(Boat)
Washer::washBoat( myVehicle );
}

Yes - no-no - not extensible. Code like this tends to proliferate and
is prone to error.
The alternative is the more correct Double Dispatch. E.g.

void Washer::wash( Vehicle myVehicle )
{
myVehicle.washer( this )
}

void Car::washer( Washer w )
{
w.washCar( this );
}

Kind of.

It would be more like:

myVehicle.DoSomthing( Wash ).

void Car::DoSomthing( Dispatcher & i_dispatch )
{
i_dispatch.WashCar( * this );
}
Now, consider we change Washer to XMLConverter and wash() to write().
This will still work, but when we want to go backwards and read XML and
write a Vehicle we need to switch on some kind of type id label anyway.
E.g.

Vehicle XMLConverter::readVehicle( XMLdoc doc )
{
Vehicle v;
string s = doc.readAttr();
if( s == "Car" )
v = new Car();
else if( s == "Bike" )
v = new Bike();
else if( s == "Boat" )
v = new Boat();

return v;
}

This is usually solved by a generic factory system like Austria C++'s
factory thing.

v = at::FactoryRegister said:
So why not just give every MyObject a typeName() method and switch or
std::map<char*, fncPtr> on that throughout all dispatchers?

That can be one way. If done correctly, the double dispatch technique
is able to pick up when you miss a case by using pure virtual methods.
In this example if a new type of vehicle is make and the method is not
implemented, it can cause a compile time error which can flag missing
actions.
 
C

Charles Bailey

The alternative is the more correct Double Dispatch. E.g.

void Washer::wash( Vehicle myVehicle )
{
myVehicle.washer( this )
}

This isn't double dispatch, the parameter is being passed by value so
if you are attempting to pass in something derived from Vehicle, this
will be "sliced" into a base Vehicle in any case.
void Car::washer( Washer w )
{
w.washCar( this );
}

OK, but this won't get called by the function above.
Now, consider we change Washer to XMLConverter and wash() to write().
This will still work, but when we want to go backwards and read XML and
write a Vehicle we need to switch on some kind of type id label anyway. E.g.

Vehicle XMLConverter::readVehicle( XMLdoc doc )
{
Vehicle v;
string s = doc.readAttr();
if( s == "Car" )
v = new Car();
else if( s == "Bike" )
v = new Bike();
else if( s == "Boat" )
v = new Boat();

return v;
}

When you are creating new objects from a serialized form you do have
to workout exactly what type of object you want to create. This has
to involve some sort of interpretation of a flat data format an making
a decision based on that. Again, this code appears to be wrong. The
return type is returning Vehicle by value, so whatever derived type is
created in the method, only a base Vehicle can be returned. The local
variable v is an object type, not a pointer, so unless the base
Vehicle class has a very unusual form of assignment operator taking a
pointer to Vehicle or derived type then this won't compile.
So why not just give every MyObject a typeName() method and switch or
std::map<char*, fncPtr> on that throughout all dispatchers?

A std::map of char* might not do what you think it does. You'd have
to find the char* in a fixed list of char* that matched the string
which you were looking for, where the fixed list was known to be the
actually char* which were used in creating the map. std::map<char*...
will be sorted by pointer value, not string value. Also, you can only
use a switch with integral or enumeration types, so you would still
need a method to convert a string to such a type.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,021
Latest member
AkilahJaim

Latest Threads

Top