Reputation: 15341
I am trying to develop a system which will be based on processing certain events and generating data. Each event will contain (possibly) several different fields and each listener will process some or them. I have the following two approaches in mind
Within the event generation class, I will register multiple event listeners, each listening for one particular value of a specific field of the event, for instance:
public class MicroListener implements Listener {
public void processEvent(Event e){
if(e.getName().equals(registeredName)) { ... }
}
This is tempting, as processing is done within the object itself, and there is no centralised processing of events, rather allowing each object to process the information. The disadvantage, possibly fatal, is the fact that each event (out of a couple of hundred thousand) will have to be broadcast to all listeners, while only tiny fraction will actually do sth with it. It will probably generate a great performance hit in the long run...
A centralised listener, which will listen and act upon all events, and delegate processing to the corresponding event processors, for instance:
public class CentralListener implements Listener {
Map<String, Processor> processorsByName;
public void processEvent(Event e){
processorsByName.get(e.getName()).process(e);
}
}
This would be faster, but it would require separate maps or collections of processors for any other part of the event e.g. processor that checks event ID etc. This is not the case in approach 1. as we would simply generate another set of listeners and register them with the event generation class.
What do you guys think about any of these? Do they make sense or would you rather advise for sth totally different?
Upvotes: 5
Views: 4395
Reputation: 2710
It sounds like you are in the beginning of something rather large and (potentially) complex. I would probably take a closer look into event-based programming before using the two more imperatively inspired approaches you mention. The idea is much the same as using listeners, only implemented asynchronously so the event-listeners can be small threads of their own that only acts when they are asked to act.
I don't know if it's applicable here, but let's assume you have different event-types that needs to be propagated to different listeners. Instead of storing the observers in one giant heap, why not make a listener for each event-type where observers can register to? If you have some core component(s) that generate events they can send them on the the listeners, that again sends them to the observers.
In my eyes the main benefit is that the responsibility is delegated to (sub-)listeners before being taken care of. In a large environment that can really help the programmer and it prevents performance bottlenecks. I believe this is also called a reactor pattern.
If you're looking for inspiration try looking at the akka framework. It's designed for efficient distributed event programming.
Upvotes: 0
Reputation: 17867
This is a common design decision, and I don't think there is a generic answer that is correct for all (or even most) cases. I could list the various trade-offs in either approach, but they are probably obvious. At a high level I would favor whatever design best fits the conceptual model (and doesn't create a mess of extraneous classes) before I would consider the possible performance implications.
If performance was of utmost concern, then a centralized controller using a large switch/case block switching on integer event ids would probably be fastest. ...and also the least flexible/maintainable over time.
You might want to review the Guava project's EventBus. It uses annotations to register event listeners and provides a very clean api for this type of event broadcasting/subscribing. The event subscribers are notified according to the type of event they declare in their method signature. It's quite slick and saves a lot of boilerplate. I don't know how well it would scale to thousands of event types, but unlike some similar libraries, the event bus is not global. You can create different event bus instances to separate event processing when it makes sense to do so.
Upvotes: 4