Mam implementację stanu State Pattern, gdzie każdy stan obsługuje zdarzenia, które otrzymuje z kolejki zdarzeń. W związku z tym klasa State
ma czystą wirtualną metodę void handleEvent(const Event*)
. Zdarzenia dziedziczą klasę podstawową Event
, ale każde zdarzenie zawiera dane, które mogą być innego typu (np. Int, string ... lub cokolwiek innego). handleEvent
musi określić typ środowiska wykonanego zdarzenia, a następnie wykonać downcast w celu wyodrębnienia danych zdarzenia. Zdarzenia są tworzone dynamicznie i przechowywane w kolejce (w ten sposób odbywa się upcasting).Jak uniknąć downcast?
Wiem, że downcasting jest oznaką złego projektu, ale jest możliwe uniknięcie go w tym przypadku? Mam na myśli wzór użytkownika, w którym stan klasy podstawowej zawierałby wirtualne procedury obsługi dla każdego zdarzenia, ale następnie ponownie należy przeprowadzić downcast w kawałku kodu, który zapisuje zdarzenie z kolejki i przekazuje go do bieżącego stanu. (Przynajmniej w tym przypadku duży switch(eventID)
będzie tylko w jednym miejscu ...). Czy wizytówka jest najlepszym sposobem (najlepsza praktyka), aby uniknąć downcastingu?
Oto pseudo-kod (ja przechodząc boost::shared_ptr
w tym przykładzie, ale rzutowanie w dół dzieje się tak):
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;
...
}
}
}
Opuszczenie wywoływane jest w wywołaniu '' Event :: accept''. Jest rozwiązany poprzez vtable do '' EventBar :: accept'' i '' this'' jest rzutowany z '' Event'' na '' EventBar'' w procesie. –
Więc nie ma innych magicznych wzorów/idiomów, aby uniknąć downcastingu? Moją jedyną troską o odwiedzających jest ilość powtarzającego się kodu, który należy zapisać. Wydaje się jednak, że to cena braku zniżek. Nie miałbym tego problemu, gdybym miał tylko jedną klasę Event i nie trzeba przechowywać pochodnych klas zdarzeń w kolejce, ale jest to nieuniknione. –
@BojanKomazec: co najmniej jednym innym sposobem jest użycie 'boost :: variant':' typedef boost :: variant Event; '. Usuwając hierarchię usuwasz downcasty :) –