Re: Clean ways to identify derived class types.
JC wrote:
I'm designing an application that uses a simple event-based model for
passing messages between objects. It's a basic "observer" setup. I
have it set up something like this (I'm just typing these here,
leaving a lot out I know):
class Event {
};
class EventListener {
public:
virtual void onEvent (const Event &);
};
class EventSource {
public:
void addListener (EventListener *);
protected:
void notifyListeners (const Event &);
};
Specific events (possibly with extra event-specific info) are derived
from Event. Note that an EventListener receives all types of events
that an EventSource generates -- it's all-or-nothing rather than
registering for specific types of events. This is important to me and
simplifies a lot of the logic throughout the application.
So, here is my question. In the various implementations of
EventListener::onEvent, some of the EventListeners handle a lot of
different event types, and the implementations end up looking rather
ugly, sort of like (again just typed here, pardon any errors):
void SomeEventListener::onEvent (const Event &e) {
const AnEvent *a = dynamic_cast<const AnEvent *>(&e);
if (a) {
handleAnEvent(a);
return;
}
const OtherEvent *b = dynamic_cast<const OtherEvent *>(&e);
if (b) {
handleOtherEvent(b);
return;
}
// and so on...
}
Are there other good ways to do this?
My first thought was to use the Visitor design pattern, in combination
with your observer:
/// In file EventListener.h
/* forward declaration of all event types */
class Event;
class AnEvent;
class OtherEvent;
class UnrelatedEvent;
/* declaration of base EventListener */
class EventListener {
public:
void onEvent (const Event &);
virtual void handleEvent(const Event &) = 0;
virtual void handleEvent(const AnEvent &);
virtual void handleEvent(const OtherEvent &);
virtual void handleEvent(const UnrelatedEvent &);
};
/// In file Event.h
#include "EventListener.h"
/* declaration of base Event */
class Event {
public:
virtual void callEventhandler(EventListener *listener) const
{ /* call the listener back with the specific event type */
listener->handleEvent(*this);
}
};
/// In file AnEvent.h
#include "EventListener.h"
#include "Event.h"
/* declaration of specific event class AnEvent. Other specific events
are similar */
class AnEvent : public Event {
public:
virtual void callEventhandler(EventListener *listener) const
{ /* call the listener back with the specific event type */
listener->handleEvent(*this);
}
};
/// In file EventSource.h
#include "Event.h"
#include "EventListener.h"
/* class EventSource is unchanged */
class EventSource {
public:
void addListener (EventListener *);
protected:
void notifyListeners (const Event &);
};
/// In file EventListener.cpp
#include "EventListener.h"
/* include headers for all event classes */
#include "Event.h"
#include "AnEvent.h"
#include "OtherEvent.h"
#include "UnrelatedEvent.h"
/* Implementation of EventListener methods */
void EventListener::onEvent (const Event &event)
{ /* Ask the event to call us back */
event.callEventHandler(this);
}
void handleEvent(const AnEvent &event)
{ /* Default behaviour: fall back to base-class handler */
handleEvent(static_cast<const Event&>(event));
}
void handleEvent(const OtherEvent &event)
{ /* Default behaviour: fall back to base-class handler */
handleEvent(static_cast<const Event&>(event));
}
void handleEvent(const UnrelatedEvent &event)
{ /* Default behaviour: fall back to base-class handler */
handleEvent(static_cast<const Event&>(event));
}
A specific EventListener class would override the handleEvent functions
for the events that the class is interested in (plus the
handleEvent(const Event&) overload as I made that one mandatory).
Speed-wise, the (sequence of) dynamic_cast<> operations is replaced by
one additional virtual call (and the assumption that there is no
multiple inheritance among the Event classes).
The big drawback of this method is that the base EventListener class has
to know about all the possible Event classes in order to provide all the
required overloads of handleEvent().
<snip>
Thanks!
J
Bart v Ingen Schenau
--
a.c.l.l.c-c++ FAQ: http://www.comeaucomputing.com/learn/faq
c.l.c FAQ: http://c-faq.com/
c.l.c++ FAQ: http://www.parashift.com/c++-faq-lite/