Ich habe eine Implementierung eines Zustandsmusters, wobei jeder Zustand Ereignisse verarbeitet, die er aus einer Ereigniswarteschlange erhält. Basis State
Klasse hat daher eine rein virtuelle Methode void handleEvent(const Event*)
. Ereignisse erben die Basis Event
Klasse, aber jedes Ereignis enthält seine Daten, die von einem anderen Typ sein können (z. B. int, string ... oder was auch immer). handleEvent
muss den Laufzeittyp des empfangenen Ereignisses ermitteln und dann downcast ausführen, um Ereignisdaten zu extrahieren. Ereignisse werden dynamisch erzeugt und in einer Queue gespeichert (so erfolgt Upcasting findet hier statt ...).Wie vermeide ich Downcast?
Ich weiß, dass Downcasting ist ein Zeichen für ein schlechtes Design, aber ist es möglich, es in diesem Fall zu vermeiden? Ich denke an Visitor Muster, wo Basisklasse State virtuelle Handler für jedes Ereignis enthalten würde, aber dann wieder downcast muss in dem Stück Code stattfinden, die Ereignis aus einer Warteschlange entnimmt und es an den aktuellen Status übergibt. (Zumindest in diesem Fall wäre der große switch(eventID)
nur an einer Stelle ...). Ist das Besucher-Muster der beste Weg, um Downcasting zu vermeiden? Hier
ist der Pseudo-Code (ich vorbei boost::shared_ptr
in diesem Beispiel aber Downcasting geschieht sowieso):
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;
...
}
}
}
Der Downcast wird im Aufruf von '' Event :: accept'' gemacht. Es wird über vtable in '' EventBar :: accept'' aufgelöst und '' this' wird dabei von '' Event'' nach '' EventBar'' umgewandelt. –
Also gibt es keine anderen magischen Muster/Idiome, um Downcasting zu vermeiden? Meine einzige Sorge mit Besucher ist die Menge an repetitivem Code, der geschrieben werden muss. Aber es scheint der Preis dafür zu sein, dass wir nicht downcasts haben. Ich hätte dieses Problem nicht, wenn ich nur eine einzige Event-Klasse hätte und keine abgeleiteten Event-Klassen in der Warteschlange speichern müsste, aber das ist unvermeidlich. –
@BojanKomazec: mindestens eine andere Möglichkeit ist die Verwendung von 'boost :: variant':' typedef boost :: variant Event; '. Durch das Entfernen der Hierarchie entfernen Sie die Downcasts :) –