Visitor has a bad reputation, partly due to some real problems
- cyclic dependency between Vistor and Visited hierarchies
- it's supposed to ruin encapsulation by exposing Visited classes internals
and partly due to the exposition in the GOF book, which emphasizes traversal of a structure rather than adding virtual functions to a closed hierarchy.
This means it doesn't get considered where appropriate, e.g for solving the double dispatch problem in statically typed languages. Example: a message or event passing system in C++, where the types of messages are fixed, but we want to extend by adding new recipients. Here, messages are just structs, so we don't care about encapsulating them. SendTo()
doesn't know what type of Message
or MessageRecipient
is has.
#include <iostream>
#include <ostream>
using namespace std;
// Downside: note the cyclic dependencies, typically expressed in
// real life as include file dependency.
struct StartMessage;
struct StopMessage;
class MessageRecipient
{
public:
// Downside: hard to add new messages
virtual void handleMessage(const StartMessage& start) = 0;
virtual void handleMessage(const StopMessage& stop) = 0;
};
struct Message
{
virtual void dispatchTo(MessageRecipient& r) const = 0;
};
struct StartMessage : public Message
{
void dispatchTo(MessageRecipient& r) const
{
r.handleMessage(*this);
}
// public member data ...
};
struct StopMessage : public Message
{
StopMessage() {}
void dispatchTo(MessageRecipient& r) const
{
r.handleMessage(*this);
}
// public member data ...
};
// Upside: easy to add new recipient
class RobotArm : public MessageRecipient
{
public:
void handleMessage(const StopMessage& stop)
{
cout << "Robot arm stopped" << endl;
}
void handleMessage(const StartMessage& start)
{
cout << "Robot arm started" << endl;
}
};
class Conveyor : public MessageRecipient
{
public:
void handleMessage(const StopMessage& stop)
{
cout << "Conveyor stopped" << endl;
}
void handleMessage(const StartMessage& start)
{
cout << "Conveyor started" << endl;
}
};
void SendTo(const Message& m, MessageRecipient& r)
{
// magic double dispatch
m.dispatchTo(r);
}
int main()
{
Conveyor c;
RobotArm r;
SendTo(StartMessage(), c);
SendTo(StartMessage(), r);
SendTo(StopMessage(), r);
}