[Dev] Event handling approaches

Here’s a writeup of what I remember about the topic from the monster thread in Development. Feel free to post what I missed, I’ll update here.

Goal: Review the event handling approach to enable future library evolution without forcing event handling code rewrites.

Event-handling code
Event handler objects are associated to a control. They have one or more onXxx functions that will get called whenever an Xxx event happens in the associated control. An event handler object can be associated to multiple controls, so it can be reused across the application - handy for making a button, a hotkey, and a menu entry all do the same thing.
Overriding works by overriding an event handler function of the control itself. Application code overrides the control class with an anonymous inner class, typically dealing with the control itself and any GUI stuff inside the override and calling a function from the enclosing class for application logic.
Both approaches require anonymous inner classes, which are icky in Java.
Overriding requires less boilerplate.
Overriding defines a standard place for event handling logic. If different structuring is desired, the event handler can always call out to a private member function of the enclosing class.
If an event handler object is desired, the control can always be overridden to have such an object and route the onXxx calls to it. It’s usually more appropriate to use an Action object provided by the enclosing class for that.
Event handler objects do a much better job at isolating event handler code from the internals of the control. Any problems arising from that can be fixed quickly though, because misbehaving code and its context are still closely connected.
Overriding. This is what TonegodGUI currently does.

Access to control state
Parameters submit the control state as multiple values.
Event objects are passed in to the event handler as parameters.
Parameterless event handlers pick up control state from the control itself.
Parameters are most convenient for application code, but adding another parameter breaks all applications that handle the event.
Event objects can be extended without breaking application code.
Event objects require creation of an extra object for each event. This could become a considerable amount during mouse movement. Some benchmarking on weak Android hardware with a bad GC would be in order to determine whether this should be considered an actual problem.
Event objects allow picking up control state from interesting points in time (such as before the event started). This isn’t a large thing because previous state is rarely used, and sometimes the really interesting previous state isn’t any point in time that the GUI library knows about; still, it can be very useful at times.
Parameterless event handlers do not need to be extended; controls can always be extended with new state without breaking existing code.
Interesting previous state would have to be saved by the event handler; the GUI library could define beforeXxx and afterXxx event handlers to allow event handling code to hook into the interesting points in time.
Individual parameters are the worst alternative. Unfortunately, that’s what TonegodGUI currently does, so we’ll have a breaking change if this is to be improved.
No conclusion yet about event objects vs. no parameters.

Both approaches require anonymous inner classes, which are icky in Java.
Well what is the problem with anonymous classes, also you can also use top level classes if you insist.

Individual parameters are the worst alternative.
Since jme is essential singelthreaded and running serial, you can just reuse a object of each type only once created, just set the new values to it and fire it again. -> If doing so adding a method to get a (deep)copy is recommended, to allow passing events into other systems running in own threads.

Separate classes require you to have an explicit member referencing the formerly-enclosing object. Setting that up is several lines of code and tends to rip logic apart.

Reusing event objects defeats their purpose: Allowing event handlers to cross-reference several of them.
I’m not seeing much of a difference between having event handlers clone event objects or letting them record parts of the control’s state.
Recording parts of the control’s state can mean a few more lines of code, but it doesn’t matter much whether you record a single member, two, or five - its just the same logic, easy to understand and write.
Event object cloning means dealing with the question how deep to clone. This is known to cause conceptually difficult bugs, and hard to explain to average programmers. The best approach to deal with that is to explicitly pick up those members of the event objects that you need - which is essentially the same as with picking up members from the control, just from a different object.
In the end, I don’t see the advantage of event objects.

From my Swing coding, I can report that I rarely ever use the data from the event object, because if I did, the workflow would be like this:

  1. Check if the data that I need is in the event object. More often than not, it isn’t, so I go to the control anyway.
  2. If the data is there, I trace where the data comes from. It might have been transformed, or be from a previous point in time. Usually, I find it easier to simply pick up the control state.
    From my Windows coding, I can report that event parameters (usually in the form of event objects) are in fact indispensable, but that’s mostly because Windows sends events before the control is back to a consistent state, and doesn’t document these inconsistencies. I don’t see any reason to follow that route in TonegodGUI though.

There is some interesting stuff with anonymous inner classes coming in the new versions of java :slight_smile:

Closures, yes. I hope they will get them right; generics didn’t really work out.
Are closures compatible with Java 5 JVMs? Because that’s what JME currently targets.

Closures will require java 8. There is some jvm trickery involved, but mainly because of the huge library changes related to them. Which will probably mean that android will not get them for a long time.

That said, they are made to be compatible with non-lambda aware libraries, as replacement for SAM types (single abstract method interfaces). This means that as long as your library is waiting for listeners which have just single method in them, you will be able to pass lambdas from java 8 environment to it without problems. It would mean it is possible to use lambdas in your app, just not in jme3 core implementation.

What @abies said :slight_smile:

And generics are really powerful, I like and use them a lot, so not sure why you think they didn’t work out…

Generics are far too complicated.
Angelica Langer’s FAQ about generics now has 251 pages of PDF (not counting TOC, index, etc., just the raw FAQs). You need to have read all of it and understood most of it to really work with generics.
Contrast that to and FPL. Generics are called “type parameters” there and usually described in two to five pages.

Also, I once tried to genericize Nifty, and it was really icky. Tons of “self type” parameters. In the end, it worked but definitely wasn’t worth it.

Generics are great for simple containers, but they don’t work very well beyond three or four interdependent types.

And, of course, type erasure. Looks relatively harmless at first sight, but it can complicate things REALLY horribly.

Slightly unfair to compare functional to object oriented languages. Complication in java comes from getting it to work with inheritance. If you remove inheritance and interfaces from java, generics will be also quite simple :wink: But yes, they are non-trivial to get right and turn very ugly if you go bit too far with them

People who try to use generics like C++ templates (where the generic typing affects the behavior of the class) will routinely be disappointed. It wasn’t really the point, though. It’s about compile-time type safety and avoiding unsafe runtime casts. It would be like in C++ wanting to know programmatically if you are const or not.

Writing a generic-based API for something should be a little hard. After all, you are attempting to take care of all of the typing problems on behalf of the caller. Where I think Java generics fails is the truly baffling error messages that the caller can get when they screw things up. This is a problem for lots of otherwise nice silver bullets: C++ templates, Groovy scripts, etc… Their ease of use is matched only by the obtuseness of their error messages.

I hear that the clang compiler has error messages that are good enough to take the horror out of C++.
I haven’t tried it myself though. Somehow JME prevented me from doing anything serious in any other language… :smiley:

But… we should return to event handling approaches, shouldn’t we?

I’ll leave this discussion up to those smarter than I. I am following this, though!