Tengo una implementación de Patrón de estado donde cada estado maneja los eventos que obtiene de una cola de eventos. Por lo tanto, la clase base State
tiene un método virtual puro void handleEvent(const Event*)
. Los eventos heredan la clase base Event
pero cada evento contiene sus datos que pueden ser de un tipo diferente (por ejemplo, int, cadena ... o lo que sea). handleEvent
tiene que determinar el tipo de tiempo de ejecución del evento recibido y luego realizar downcast para extraer datos de eventos. Los eventos se crean dinámicamente y se almacenan en una cola (por lo que upcasting tiene lugar aquí ...).¿Cómo evitar el downcast?
Sé que el downcasting es un signo de un mal diseño, pero es posible evitarlo en este caso? Estoy pensando en Patrón de visitante donde el estado de la clase base contendría controladores virtuales para cada evento, pero luego se tendrá que realizar un downcast en el fragmento de código que dequeue el evento de una cola y lo pasa al estado actual. (Al menos en este caso grande switch(eventID)
sería solo en un lugar ...). ¿El patrón de visitante es la mejor manera (práctica recomendada) para evitar la bajada?
Aquí es el pseudo-código (estoy pasando boost::shared_ptr
en este ejemplo, pero downcasting pasa de todos modos):
enum EventID
{
EVENT_1,
EVENT_2,
...
};
class Event
{
EventID id;
public:
Event(EventID id):id(id){}
EventID id() const {return id;}
virtual ~Event() = 0;
};
class Event1 : public Event
{
int n;
public:
Event1(int n):Event(EVENT_1), n(n){}
int getN() const {return n;}
};
class Event2 : public Event
{
std::string s;
public:
Event2(std::string s):Event(EVENT_2), s(s){}
std::string getS() const {return s;}
};
typedef boost::shared_ptr<Event> EventPtr;
class State
{
...
public:
...
virtual ~State() = 0;
virtual void handleEvent(const EventPtr& pEvent) = 0;
};
class StateA : public State
{
...
public:
void handleEvent(const EventPtr& pEvent)
{
switch(pEvent->id())
{
case EVENT_1:
int n = boost::static_pointer_cast<Event1>(pEvent)->getN();
...
break;
case EVENT_2:
std::string s = boost::static_pointer_cast<Event2>(pEvent)->getS();
...
break;
...
}
}
}
El downcast se hace al llamar a '' Event :: accept''. Se resuelve a través de vtable con '' EventBar :: accept'' y '' this'' se convierte de '' Event'' a '' EventBar'' en el proceso. –
Entonces, ¿no hay otros patrones/modismos mágicos para evitar el downcasting? Mi única preocupación con Visitor es la cantidad de código repetitivo que debe escribirse. Pero parece que es el precio de no tener downcasts. No tendría este problema si tuviera solo una clase de evento y no fuera necesario almacenar las clases de eventos derivados en la cola, pero eso es inevitable. –
@BojanKomazec: al menos otra forma es usar 'boost :: variant':' typedef boost :: variant Event; '. Al eliminar la jerarquía, eliminas los downcasts :) –