Simple Event Handling
Revising the IEventHandler interfaceAs it stands, the interface is still fairly simple. We now need to add certain functionality to handle creating the linked list, as well as a couple extra useful methods. Here is the final full code for the IEventHandler interface: class IEventHandler { public: virtual void EventHandler(const Event &e) = 0; // Mutator and selector IEventHandler * GetNextHandler(void) {return _nextHandler;} void SetNextHandler(IEventHandler *next) {_nextHandler = next;} IEventHandler() : _nextHandler(0) { EventDispatcher::Get()->RegisterHandler(this); } protected: void SendEvent(int eventType, int arg1 = 0, int arg2 = 0) { EventDispatcher::Get()->SendEvent(eventType, arg1, arg2); } private: IEventHandler *_nextHandler; }; I've added a pointer to the next handler (_nextHandler), as well as mutator and selector methods for this variable. As noted above, these two methods are used by the EventDispatcher to update, and cycle through the list of devices. Using a linked list allows for the list to vary in size without worrying about dynamically allocating memory, or knowing beforehand how many devices will listen for events. (Early versions of this system used these less elegant methods). I've also added a constructor for this interface. A class which implements the IEventHandler interface will automatically call IEventHandler's constructor when its own constructor is invoked. In this manner, one does not have to remember to register an object as a listener as IEventHandler's constructor does so automatically. A class listening to events doesn't need to know how the event dispatching is implemented, nor does it have to rely on a third party to register it with the dispatcher. The last method (SendEvent) is used to further abstract the EventDispatcher. Any class implementing this interface doesn't need to know anything about the dispatcher. It just calls its own SendEvent method when it needs to generate an event. Putting It All TogetherNow, I'll illustrate the usage of this system with a relatively short example. Assuming we have two classes A and B defined as follows: #include "IEventHandler.h" class A : public IEventHandler { public: void EventHandler(const Event &e) { switch (e.Type) { case E_NEWGAMEEASY: cout << "Class A handling E_NEWGAMEEASY event" << endl; SendEvent(E_INCREMENTSCORE); break; case E_PAUSEGAME: cout << "Class A handling E_PAUSEGAME event" << endl; break; } } }; class B : public IEventHandler { public: void EventHandler(const Event &e) { switch (e.Type) { case E_INCREMENTSCORE: cout << "Class B handling E_INCREMENTSCORE event" << endl; break; case E_PAUSEGAME: cout << "Class B handling E_PAUSEGAME event" << endl; break; } } }; We compile and execute the following piece of code: #include The output of the program will be: Main fct sending E_NEWGAMEEASY event Class A handling E_NEWGAMEEASY event Class A sending a E_INCREMENTSCORE event Class B handling E_INCREMENTSCORE event Main fct sending E_PAUSEGAME event Class B handling E_PAUSEGAME event Class A handling E_PAUSEGAME event ConclusionNow we have a simple Event Handling/Dispatching system. There are certain performance issues to consider when using this system. Spending too much time in the EventHandler methods basically slows down the overall event dispatching. One solution is to spawn off a thread for each event handler, but as the number of events and event handling objects increase, the overhead for creating and deleting threads will become significant. The best solution is to use the event handler to set flags or store important variables (such as mouse coordinates), and make use of that during the update/render loop. The use for this type of system is fairly obvious. Aside from handling mouse and keyboard events (and abstracting those events), general events can be created and shared across the program. Sound events can be generated for specific situations (such as a collision, or a menu selection). Different components of a game might need to be informed when a new game is created or when the game play is paused. The biggest advantage of this system, in my opinion, is that it allows a higher level of abstraction between modules. I've included all c++ header and source code I've mentioned in this article, and you're free to use it in any program you want. Any questions or comments, please email me. |
|