Separating event handling from event filtering

One thing that bugs me with event handling is that usually I’m not interested in every event that comes my way. This makes my event handlers more about filtering events than handling them — for example checking that shift is pressed when a KeyboardEvent comes around, or checking that a MouseEvent is above a specific area of a panel. To separate the concerns of filtering and handing I’ve come up with some AOP-like looking ActionScript. Read on for the full explanation.

The problem

Good application architecture is about separation of concerns and one example where separation of concerns usually isn’t fulfilled is event handlers, which tend to be just as much about determining if the event is relevant as actually responding to (i.e. handling) the event.

An example

A typical KeyboardEvent listener method looks something like this:

private function onKeyUp( event : KeyboardEvent ) : void {
  if ( event.keyCode == Keyboard.ENTER ) {
    commitWhateverWeAreDoing();
  }
}

…and quickly gets complex if you listen for multiple combinations of keys. But it’s not the handling that gets complex but the code that checks if you actually want to handle the event or not, and how you want to handle it. You end up with lots of complex expressions in long and nested if-else statements — a classic example of a fragile implementation.

You may at this point feel that you don’t want to write any method called onUserCommit at all, but rather something called handleCommit, that’s fine, but I’m stuck in the on-prefixing of my event handlers, and I kind of like it.

Because the event system in Flex (and most other GUI frameworks) is so low-level (key presses and mouse clicks and so on) it can be hard to listen to higher-level events that capture the user’s intent or actions (like commit action, new line, ok, cancel) without having to write filtering code (like the if-statement in the code above). It also interferes with the naming of event handlers, the event handler above could have been called onReturnUp, or even more intent-oriented: onCommit. That last handler could also handle not only key events but also mouse clicks and other low-level events that could mean “commit”.

I’m not saying that the event system in Flex is bad, far from it. Being low-level is not bad in itself, somewhere the low-level stuff has to be done. What is bad is when you, the programmer, have to spend time writing too much low-level code. You should always aim to abstract away all the low-level things so that your code describes what you want to do, not how to do it. That way your code will become easier to understand and maintain.

I would guess that your event handlers look like mine have done: more or less generically named after the event they listen to and containing a lot of complex if statements. The actual event handling is done in other methods which the event handler calls. Usually many different event handlers call the same methods, because many different events mean the same thing (e.g. instead of onCommit I have a commit which is called by onKeyUp and onClick depending on the circumstances.

An even more concrete example

Consider a form: it has some text fields, a button which sends the form and another which cancels and returns to a previous application state. Pressing the return key should be the same as clicking the send button and pressing escape is the same as clicking the cancel button. On a higher, conceptual, level there are two events: “send” and “cancel”.

As I used to code this I registered event handlers listening for click event from the buttons, let’s call them onSendButtonClicked and onCancelButtonClicked. Then I had a listener that listened for key events from the form, let’s call it onKeyUp. The button event handlers would call methods called something like sendForm and cancel respectively, and the key event handler would call either sendForm, cancel or nothing depending on which key was pressed.

In code:

form.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
cancelButton.addEventListener(MouseEvent.CLICK, onCancelButtonClicked);
sendButton.addEventListener(MouseEvent.CLICK, onSendButtonClicked);

// ...

private function onKeyUp( event : KeyboardEvent ) : void { if ( event.keyCode == Keyboard.ENTER ) { sendForm(); } else if ( event.keyCode == Keyboard.ESCAPE ) { cancel(); } }

private function onCancelButtonClicked( event : MouseEvent ) : void { cancel(); }

private function onSendButtonClicked( event : MouseEvent ) : void { sendForm(); }

private function cancel( ) : void { // ... }

private function sendForm( ) : void { // ... }

Please note that none of the three event handlers actually handle the events, they merely delegate the handling to the sendForm and cancel methods. In practice, sendForm and cancel are the event handlers, the other methods are just filters.

There’s nothing deeply wrong with that way of doing it. It works fine, but it bugs me to have all these event handlers that don’t actually do anything, and the if-else in onKeyUp is very fragile (meaning it doesn’t handle changing requirements very well). Also, the type of the event (e.g. KeyboardEvent and MouseEvent) is irrelevant to the event handling, it’s only used by the filtering (e.g. looking at which key was pressed).

Instead of registering for low-level events like “key up”, I want to listen for events like “return pressed”, “escape pressed” and “button clicked”, and I don’t want to have separate handler methods for each. Instead I would like to be able to establish a rule that pressing return or clicking on the send button means “send form” and should be handled by a method called onSendForm and consequently that pressing escape or clicking on the cancel button means “cancel”, which should be handled by a method called onCancel. This requires that there is something in-between the low-level events and my higher-level model.

To create that something I would either have to extend the Flash player runtime to generate more high-level events, or come up with another solution that decoupled the filtering from the handling. To extend the Flash runtime is neither possible nor desirable. You would most likely end up with a mess of way too many events, especially since high-level events like these tend to be application specific and not at all easy to generalize, “cancel” and “send” don’t make sense in every form and some forms also have a “reset”.

As you might have guessed I chose another solution.

The solution

I have a special place in my heart for functional programming and higher order functions. Some day I’m going to come around to releasing my higher order functions library that I use in my own projects, but that day is not today. I will, however, show you a function called guarded that I used to solve this problem.

guarded is a function that transforms a function so that it has a “guard” which determines if it is actually going to be called or not when it’s called. If that sounds confusing think of it as an encapsulated if statement: when the guarded function is called it checks if it should call the actual function, depending on some test. The test is yet another function that receives the same input as the guarded function and returns true if the guarded function should be called.

Let’s look at the code. It might not be entierly obvious from looking at it that it will help with the problem of event handlers, but I’ll get to that (trust me, I know I use too many words and have too many asides, but I do get around to the point in the end).

package {
  public function guarded( func : Function, guard : Function ) : Function {
    return function( ...args ) : void {
      if ( guard.apply(null, args) ) {
        func.apply(null, args);
      }
    }
  }
}

The function could be called createGuardedFunction or makeGuarded, which might have made more sense. The reason for the name “guarded” is that it fits into the nomenclature of my internal higher order functions library where the functions often are often used together and passed to each other, which means that for once terse naming leads to more readable code.

If you are used to anonymous functions (sometimes also called “closures”) and functions returning new functions the code above shouldn’t be too hard to understand. If you’re not used to this sort of thing you might wonder what the hell is going on, that is understandable.

What happens is that when called, the function guarded returns a new function (remember, functions are objects and can be created and passed around as any other objects), which, when called, will call the function guard and then func, if guard returned true.

apply is a way to call a function object, look it up in the documentation if you haven’t seen it before.

Both func and guard receive the same parameters, so basically guard does the same as the if statement in the event handler in the first code example above, it determines if the call is going to be handled or not. This follows the object-oriented maxim of encapsulating the thing that varies, in this case the if-statement of the event handler.

In the words of AOP (Aspect-Oriented Programming) you could say that guarded creates an aspect at runtime where the guard function (together with the if-statement the guard function executes in) is an advice and the point-cut is at the start of the method. I haven’t been much into AOP so I may have got the concepts wrong, but it looks quite similar to me.

The example, rewritten

Using guarded it’s possible to implement the form example the way I wanted (I have talked about the key “return”, but it seems like Flash treats the return and enter keys as one and the same):

var enterKeyListener : Function = guarded(onSendForm, enterKeyGuard);
var escKeyListener : Function = guarded(onCancel, escKeyGuard);

form.addEventListener(KeyboardEvent.KEY_UP, enterKeyListener); form.addEventListener(KeyboardEvent.KEY_UP, escKeyListener); cancelButton.addEventListener(MouseEvent.CLICK, onCancel); sendButton.addEventListener(MouseEvent.CLICK, onSendForm);

// ...

private function enterKeyGuard( event : KeyboardEvent ) : void { return event.keyCode == Keyboard.ENTER; }

private function escKeyGuard( event : KeyboardEvent ) : void { return event.keyCode == Keyboard.ESCAPE; }

private function onCancel( event : Event ) : void { // ... }

private function onSendForm( event : Event ) : void { // ... }

In this example we can see that the filtering is separated from the handling, and that now there is no need for the sendForm and cancel methods, they have instead been merged into onSendForm and onCancel. The fragile if-statement is also gone and replaced with the tests in the guards.

The two event handlers are called both for key events and mouse events, so their arguments are typed simply as Event, and in most cases they wouldn’t care about the event object at all. The only place where the event type matters is in the guards.

Shorter and more reusable code

Even though I didn’t have any intention of minimizing the amount of code, I more or less got that as a side effect, and if I wanted to I could do even more. A big difference I made in this version of the code is that I replaced something non-reusable with something reusable. The old onKeyUp was not reusable, but the guard functions are so general that they could be used throughout the application (and in any application, as a matter of fact) wherever escape and return key presses need to be filtered out from other key events.

What’s more is that you can write a general and parameterizable version of the guards so that in not much more code than required to write one of them you could have a version that could filter out any key press. Add the following function to the mix:

function createKeyGuard( acceptedKey : uint ) : Function {
  return function( event : KeyboardEvent ) : Boolean {
    return event.keyCode == acceptedKey;
  }
}

…and you can replace enterKeyGuard and escKeyGuard with the expressions createKeyGuard(Keyboard.ENTER) and createKeyGuard(Keyboard.ESCAPE), respectively.

Taking this even further you can write a function which can create guards that accept complex key combinations instead of just single keys, but that I leave as an exercise.

It’s not just about key events

The reason why I wrote this article was that I was fed up with key event handlers that got messed up by too complex tests, but it’s not just about key events. guarded can be used for any event which needs some kind of filtering before being handled, and its use is not even limited to event handling. Even though I wrote it to solve this particular problem, I also wrote it to be general and as it is it could be used in any circumstance where you want to decouple checking of the arguments to a method from what the method should actually do.

One example is when you have a number of asynchronous calls, for example loading of a number of files simultaneously, and you want to do something when all of the calls are finished. Using guarded, what you do is that you write a test that determines if all are finished, one handler that should run when that is the case, and then pass guarded(handler, guard) as a callback or listener to all the asynchronous calls. The guard will be called when each asynchronous call is finished and when it returns true the handler is called.

I hope you find guarded useful, or at least this discussion about event handling interesting and enlightening.

15 Responses to “Separating event handling from event filtering”

  1. Daniel Gasienica Says:

    Theo,

    Great article, thanks for sharing. Where did you get the inspiration for «guards»? I am learning about Haskell right now in my CS studies. It also has the concepts of guards. Haskell, or functional programming in general, is a great inspiration and motivated me to write an introductory series for ActionScript programmers. Check out the first part on http://gasi.ch/blog/2008/03/29/functional-actionscript-part-1/

    Looking forward to reading more about your thoughts on functional programming and ActionScript!

    Regards, Daniel

  2. Ruben Swieringa Says:

    Rad, this is actually something I too have been walking into for a while now, very creative solution here. Thanks!

  3. jimmy fung Says:

    very nice post. thanks for sharing.

  4. Theo Says:

    Daniel: I have worked with Haskell in some computer science courses (I studied software engineering), but I never got very deep into it. The concept of guards more or less came to me when thinking about my ugly event handlers.

    Ruben: Seems like I have to show you my Trampoline some day, it’s your ArrayTool on speed =)

    It’s nice to see that there are others that are interested in higher order functions and functional programming in ActionScript. If you look at my blog archive I think there are one or two old posts about how to do away with loops by using functional programming concepts.

    I have a small library that I use privately which contains implementations of useful higher order functions and functional programming concepts, one day I’m going to get around to writing about them too.

  5. Stephan Bezoen Says:

    Hi Theo,

    I agree that filtering events is a pain in the butt. However in this particular case (handling keyboard events), a map approach seems to me a lot more concise (and thereby more elegant :D). Now let’s see if the comment system allows me to submit code in a legible way. For testing, I wrote this little class called KeyMapper: /** * @author stephan.bezoen */ public class KeyMapper { private var mMap : Object = new Object(); private var mStage : Stage;

    public function KeyMapper (inStage:Stage) {
        mStage = inStage;
         mStage.addEventListener(KeyboardEvent.KEY_UP, handleKeyEvent);
    }
    
    public function setMapping (inKey:uint, inHandler:Function) : void {
        mMap[inKey] = inHandler;
    }
    
    public function die () : void {
        mMap = null;
        mStage.removeEventListener(KeyboardEvent.KEY_UP, handleKeyEvent);
    }
    
    private function handleKeyEvent(e : KeyboardEvent) : void {
        if (mMap[e.keyCode]) mMap[e.keyCode]();
    }
    

    }

    As you can see in the KeyMapper.handleKeyEvent() function, the only comparison that is still needed, is to check whether a mapping is defined for the pressed key.

    To use this in an application, do the following: var km : KeyMapper = new KeyMapper(stage); km.setMapping(Keyboard.ENTER, submit); km.setMapping(Keyboard.ESCAPE, cancel);

    In my view, this approach really takes away the inner workings of the keyboard event system from the user. It’s easily readable, and very easy to extend when more keys have to be handled.

    As for multiple asynchronous events, that is a place where guards could very well come in handy. Just have to get used to the term I guess :D

  6. Theo Says:

    Stephan: yes, I agree, your solution is quite elegant. I have nothing against the if-statement in your handleKeyEvent, it is not of the kind that makes code fragile.

    guards are very general and there are absolutely better ways to solve specific cases.

  7. Functional ActionScript – Part III — RTFM / Daniel Gasienica Says:

    [...] Iconara: Separating event handling from event filtering [...]

  8. Bjorn Schultheiss Says:

    The only time this seems useful is if your repeating the same ‘if statement’ in multiple methods, otherwise you’ve simply wrapped the if statement in another function…

    I was interested to see how you were going to modify sendButton.addEventListener(MouseEvent.CLICK, onSendButtonClicked);

    but later you replaced it with sendButton.addEventListener(MouseEvent.CLICK, onSendForm);

    ahh well.

  9. Joshua E Cook Says:

    Stephan, although your KeyMapper works elegantly for associating an action with a particular keycode, the guarding method is more flexible. Consider if you wanted to associate a different action for a keycode when the SHIFT or CTRL keys are held down. This would require significant changes to your KeyMapper class, but only a simple change to the guard function is required.

  10. Marcus Stade Says:

    I LOVE this concept, thank you very much for sharing! Great blog btw, just stumbled upon it.

  11. Tom Carden Says:

    It took me a couple of attempts to fully understand the appeal of this approach, but I like it very much. It feels somehow “well rounded” :)

    If you’re still leaning towards releasing your higher order functions library, I’d gladly contribute time to helping with documentation or testing. I have some array filtering classes of my own that might fit with such a library, too.

  12. Eric Greveson Says:

    I like the guard function method over the key mapper method (which I’ve been using up to now), it’s more flexible and reminds me that I should be making use of first-class functions (I’m from a C++ background so I keep forgetting about them)! I’d like to see your higher order functions library being released too – simple, lightweight libraries that really improve the way people code are something that’s mostly missing from the Flex community at the moment (although I’ve been trying Mate recently, after your recommendation, and it’s pretty cool).

  13. Panayot Says:

    You are using kind of constructor but without new operator. The guarded returns a function object but you don’t feel it as an object.

    Also you say it is filter but use ‘guarded’ as name.

    I would prefer to use something like:

    public class EventFilter { protected var _filter:Function; protected var _function:Function; public function EventFilter(theFilter:Function, theFunction:Function) { _filter = theFilter; _function = theFunction; } public function get handler(e:Event) { if (_filter(e)) _function(e) } }

    Then assign listeners like:

    var eventFilter = new EventFilter(_isEnterKey, _onSend) addEventListener(KeyboardEvent.KEY_UP, eventFilter.handler);

    protected function _isEnterKey(e:Event) { … } protected function _onSend(e:Event) { form.send() }

    If you have getter and setters defined on the EventFilter you would be able to change the filtering function and the handler.

    Also that way it’s clear that you create an object that references the objects that own the filter and the function properties.

    Some coders may miss the fact that the function returned by: guarded(obj1.someFilter, obj2.somehandler); would most probably reference the obj1 and obj2. If you add the guarded listener on the stage for example you will have to remove the listener later or obj1 and obj2 will never be garbage collected.

    Anyway it’s only my point of view. You’ve done nice work on this article and I would love to see your higher order functions library :)

  14. Theo Says:

    It’s not a constructor in the object-oriented sense. It’s a function that returns a lambda, it’s a common functional programming idiom.

    Something can be a filter without having the name “filter”, and “filter” is an overloaded term and can mean different things. “guarded” is a filter because it filters, just like a guard is a filter since he or she says who can enter and who cannot. But a guard is not a water filter, nor is a cigarette filter, and none of those is the list filtering function “filter”.

    Your class-based version kind of shows exactly why I prefer to solve a problem like this using higher order functions. That code is so much unnecessary extra code that doesn’t do anything besides creating a functor (i.e. an object that acts as a function). I understand that not every one knows functional programming idioms and get them straight away, but if you work in a language that has higher order functions why avoid using them? If someone doesn’t get the functional approach to solving a problem it’s too bad for them. If we shouldn’t do this or that because some may not understand we might as well limit ourselves to the feature set of Fortran.

    “If you add the guarded listener on the stage for example you will have to remove the listener later or obj1 and obj2 will never be garbage collected.”

    This is true for any event listener, your EventFilter too, so it’s hardly an argument. Use weak references or keep the listener around, that’s your options whichever way you decide to solve the problem.

  15. mzx Says:

    nice one. and as u remember some flex components (TextInput) has different event handler for enter.. so to do as many events as u need is the correct approach imao

Leave a Reply