Lexical Dispatch in JavaScript

Using a big switch statement to fake polymorphism is evil. Everybody knows that. But how do you get rid of it?

Let’s say you’ve got a method that receives a string describing some state (“normal”, “error” etc.). And depending on the value of that string (state) you want to react.

If you ask around, an OOP afficionado might tell you to replace the switch with classes and use polymorphism. Yeah, well, that’s not the greatest of solutions – you just replace an ugly statement with a handful of ugly classes. Functional programming fan would tell you to use pattern matching (A.K.A. “case statement on steroids”), which would work nice…only you’re stuck with an imperative language. Tough luck. So, isn’t there another way?

No, there isn’t.

Nah, just kidding. Turns out we can use a neat little design pattern familiarly reffered to as “lexical dispatch” (see this article about a similar pattern in Python).

Observe:

myDispatcher = {};
myDispatcher.dispatch = function (eventName /*, other args...*/ ) { 
  
  // Get the handler. 
  var handlerName = "handle" + _.string.capitalize(eventName.toLowerCase());
  var handler = this[handlerName]; 
  
  // Handle unknown event.
  if (!_.isFunction(handler)) { 
    throw _.string.sprintf("ERROR: Handler for event %s not found!", eventName);
  }
  
  // Call the handler.
  handler.apply(this, _.tail(arguments));
};

myDispatcher.handleNormal = function(msg, details) {
  console.log(_.string.sprintf("Everything is %s...%", msg, details));
};

myDispatcher.handleError = function(msg, details) {
  console.log(_.string.sprintf("%s...%s", msg, details));
}; 

myDispatcher.dispatch("normal", "just, you know...fine");
myDispatcher.dispatch("error", "AAAAARHG!", "AAAAAAAAAAAARGH!");

(We’re using underscore.js and underscore.string.js in the example).

The important piece is the myDispatcher.dispatch method. It takes any number of arguments, but the first one is a state string. We use it to construct the name of the handler that will be invoked for that particular state.

So:
myDispatcher.dispatch(“normal”,…) is delegated to handleNormal.
myDispatcher.dispatch(“error”,…) is delegated to handleError.
myDispatcher.dispatch(“foo”,…) would be delegated to handleFoo.

If we want to add a new state, it’s as simple as adding a new method to myDispatcher. No more searching for that ugly case statement.

And a big boo-yah to static languages :-)

Tomas Brambora

Tomas Brambora