Jump to content
  • Advertisement
Sign in to follow this  
Nairb

Alternative to Singleton/Global for Event Handler

This topic is 4277 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I have two major classes that I make heavy use of in my code: EventManager and EventListener (an interface). Classes which implement EventListener can register themselves with the EventManager, which they can then use to listen to specific events. Any subsystem can post events to the EventManager. Right now my EventManager is a Singleton, which I would like to get away from. It made sense to me for the following reasons: (1)There's only ever going to be one 'master' EventManager. (2)The code in the EventManager is relatively fixed; that is, I should never need to inherit from it. (3)A lot of my classes need to post events to the EventManager. Passing an EventManager around as a parameter to, say, constructors of these classes quickly becomes very cumbersome (and sometimes introduces illogical coupling - when a child in a heirarchy needs an instance and the parent doesn't, the parent still has to take it to pass it down to the child). But still, the Singleton approach introduces global nastiness and makes for some unclear dependencies. So I'm looking for insights into a cleaner design. There's a lot of talk around here about why Singletons suck, but I'd like to get ideas for solutions to the problems they create. :-) The only solution I've thought of so far is to introduce an "EventManagerFactory" with a method "CreateEventManager" that simply returns the same EventManager each time it's called, but that seems a little... hackish to me. Cheers, --Brian

Share this post


Link to post
Share on other sites
Advertisement
Quote:
Original post by Nairb
Right now my EventManager is a Singleton, which I would like to get away from. It made sense to me for the following reasons:
(1)There's only ever going to be one 'master' EventManager.
(2)The code in the EventManager is relatively fixed; that is, I should never need to inherit from it.
(3)A lot of my classes need to post events to the EventManager. Passing an EventManager around as a parameter to, say, constructors of these classes quickly becomes very cumbersome (and sometimes introduces illogical coupling - when a child in a heirarchy needs an instance and the parent doesn't, the parent still has to take it to pass it down to the child).

But still, the Singleton approach introduces global nastiness and makes for some unclear dependencies.


Singletons don't suck. They have their place. They also tend to get highly abused.

In your case, with your requirements, there seems to be no need for more de-coupled design.

Events fall under general communication concepts. As such, they need to be globally accessible. Handling of events is what needs to be decoupled, and that seems to be done.

If you find youself with many different managers, then something like slot system works. You have a global slot manager, and when an objects needs to either produce or receive data, it obtains a reference to apropriate object from this global instance.

Coupling event system too tightly into your code defeats the purpose of de-coupling - you might as well just pass the actual references of handlers.

Share this post


Link to post
Share on other sites
An EventManagerFactory which returns the same instance every time is still a singleton.

So, you're looking at passing a communications channel to your classes. The first step is understanding why a child class needs a channel while the parent class doesn't — this is a design inconsistency which should be solved in two ways:

  • It makes sense for the parent class to require a channel. In that case, reformulate the design so that the channel is a constructor argument to the parent class.
  • It doesn't make sense for the parent class to require a channel. In that case, you should probably split the child class in two, let the parent handle the channel-independent part, and put the channel-dependent part somewhere else.


Can you give a few examples of parents which don't need the channel, but children which need it?

Share this post


Link to post
Share on other sites
Quote:

Any subsystem can post events to the EventManager.


As long as this is a design requirement, you're going to end up with a global/singleton/static hack. I don't know what you often use it for, or how complicated the fix would be but my first instinct would be to localize the EventManager to the subsystem. Each piece of a subsystem could then listen to its local Manager.

If you still then need some sort of globalish events, the subsystems could listen for a small subset in a EventManager in your game/application class. You'll still kinda have a global but you cut down on the code it's visible to greatly.

Share this post


Link to post
Share on other sites
Why have an EventManager class at all? Wouldn't it make more sense to have individual Events which are handled separately?

Share this post


Link to post
Share on other sites
Quote:
Original post by Sneftel
Why have an EventManager class at all? Wouldn't it make more sense to have individual Events which are handled separately?


It's probably to help with the decoupling. A system can say "post a PLAY_SOUND message" without having to know about who consumes the event. It sounds like the event manager is what keeps track of what listeners listen to what events.

As for the OP, it might be useful to decouple the event manager from its interface. That way, anything that needs to publish events just talks to the interface (which can be as simple as a couple of functions or static methods), and you don't need to pass in a reference to the actual manager (which does have a bit of a painful feel to it).

Share this post


Link to post
Share on other sites
Quote:
Original post by Replicon
It's probably to help with the decoupling. A system can say "post a PLAY_SOUND message" without having to know about who consumes the event. It sounds like the event manager is what keeps track of what listeners listen to what events.

Erm, I don't think you took my meaning. If there's going to be a PLAY_SOUND message which "someone" posts and "someone" listens to, why not have Event playSoundEvent; which stands on its own rather than being in a std::map somewhere in the bowels of EventManager?

Share this post


Link to post
Share on other sites
Quote:
Original post by Sneftel
Quote:
Original post by Replicon
It's probably to help with the decoupling. A system can say "post a PLAY_SOUND message" without having to know about who consumes the event. It sounds like the event manager is what keeps track of what listeners listen to what events.

Erm, I don't think you took my meaning. If there's going to be a PLAY_SOUND message which "someone" posts and "someone" listens to, why not have Event playSoundEvent; which stands on its own rather than being in a std::map somewhere in the bowels of EventManager?


True, but I was thinking a bit more of a generic case, where any number of listeners can listen for any combination of events, and be managed dynamically (stop listening to some, etc.) Then, there is going to be a many-to-many mapping that needs to be represented in the system somehow. So when something creates Event playSoundEvent; and sends it off into the ether, it needs to know how to find its way to the x listeners. How does your solution achieve that? I'm not trying to instigate, just I come from a different sector of the software industry, and am hoping to learn a new technique :D.

Share this post


Link to post
Share on other sites
Quote:
Original post by Replicon
True, but I was thinking a bit more of a generic case, where any number of listeners can listen for any combination of events, and be managed dynamically (stop listening to some, etc.) Then, there is going to be a many-to-many mapping that needs to be represented in the system somehow. So when something creates Event playSoundEvent; and sends it off into the ether, it needs to know how to find its way to the x listeners. How does your solution achieve that?

With no problem at all. If a listener decides it wants to quit listening to an event, it simply unregisters itself from that event. If it wants to register itself with multiple events, it calls register on those events. There's really very little difference in functionality; basically the only difference is that instead of calling eventManager.registerForSpecificEvent("SOME_EVENT_UNIQUE_IDENTIFIER", this) you call someEvent.register(this). Different events are different Things; they shouldn't pretend they're all bits of a larger object.

Share this post


Link to post
Share on other sites
Quote:
Original post by Sneftel
With no problem at all. If a listener decides it wants to quit listening to an event, it simply unregisters itself from that event. If it wants to register itself with multiple events, it calls register on those events. There's really very little difference in functionality; basically the only difference is that instead of calling eventManager.registerForSpecificEvent("SOME_EVENT_UNIQUE_IDENTIFIER", this) you call someEvent.register(this). Different events are different Things; they shouldn't pretend they're all bits of a larger object.


Ah, I see, so really, your Event playSoundEvent; is really more of a global-scoped thing than one whose lifetime is the sending of one message.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!