Dispatcher Pattern Examples

Kwindla Hultman Kramer
6 min readJun 26, 2017

--

Lately we’ve been refactoring some of our core code. Refactoring is always a good excuse for architectural conversations. One thing we’ve been talking about is a “dispatcher” pattern that I use a lot, especially when prototyping new code.

Here’s a contrived example of this pattern:

let dispatcher = {
evtComplicated: (argsAndStuff) => {
console.log('evtComplicated called', argsAndStuff);
},
justGettingStarted: () => {
console.log('Hello, World!');
}
}
let compThingaBob = new ComplicatedLibraryService({ dispatcher });
dispatcher.justGettingStarted();
// ---- the code inside ComplicatedLibraryService ----class ComplicatedLibraryService {
constructor({ dispatcher }) {
this.dispatcher = dispatcher;
setInterval(() => this.complicatedStuffHappens(new Date()), 2500);
}
complicatedStuffHappens(argsAndStuff) {
// ...
if (this.dispatcher.evtComplicated) {
this.dispatcher.evtComplicated(argsAndStuff);
}
}
}

The critical part is the dispatcher object, which is just a table of named function references. The dispatcher is shared between pieces of code that need to communicate. As its name suggests, it’s an event dispatcher of sorts.

The reason this pattern is so useful is that it’s a lightweight, flexible way to accomplish three valuable things: loose coupling between components, encapsulation of that coupling, and inversion of control.

Loose coupling is the idea that it’s good to glue big components together with little bits of intermediary code, rather than write them so they call each other directly.

For example, maybe I’m working with a user interface framework, and I’ve written a bunch of custom code to turn mouse gestures into input events. Rather than design my code to call the UI framework’s API directly, I can use the dispatcher pattern.

very simple dispatcher use case

With this loose coupling — the dispatcher sitting in between the MouseGesture library and the UI framework — I don’t have to change much code to use my library with a different UI framework.

As my code gets more complicated, I can extend the dispatcher to make the loose coupling work both ways. And while I’m at it, I can add another helper library and some additional events.

the dispatcher gets a little fancier

Encapsulation means organizing related functionality together and hiding some of the implementation details behind an abstraction.

The examples given above show encapsulation as well as loose coupling. It’s often pretty easy to write a single chunk of dispatcher code that works with several similar libraries. You hide the differences between the libraries inside the dispatcher code. It’s especially easy to do this if you have the flexibility to design your application code around the dispatcher pattern from the beginning.

At Pluot, for example, we’ve prototyped with five different javascript messaging libraries over the past two years. We’ve also refactored our RTCPeerConnection code at least that many times. The messaging libraries and the RTCPeerConnection code have to talk to each other. Because we use dispatcher objects to mediate that communication, we can swap libraries in and out relatively easily.

Inversion of control is just a fancy, specific way of talking about callbacks. Wikipedia has a good, if slightly stodgy and old-fashioned, overview.

The basic idea is that instead of calling library code, your library code calls you.

You may be scratching your head saying, “but that’s how most libraries work, right? I mean, every single HTML DOM object has a bunch of events you can install callbacks on just by using addEventListener(). Or, if you’re really old school, you can set the onfooevent object properties directly.”

And you’re right. Inversion of control is so common, these days, that we’ve forgotten we ever needed a special name for it.

And that’s mostly for the good. However, DOM-style individual callbacks don’t, by themselves, do much to help you create code that is loosely coupled or nicely encapsulated. Fortunately, javascript makes it so easy to pass around function references that it doesn’t really matter. (Abyssus abyssum invocat.)

Here’s an example of how to translate between a dispatcher and a callback-tastic HTML5 object. We set up a dispatcher and then use bind() to create function references we can use as event callbacks.

let dispatcher = {
evtVideoPlaying: (videoElement, e) => { ... },
evtVideoPause: (videoElement, e) => { ... },
evtVideoLoadedMetada: (videoElement, e) => { ... },
};
let video = document.createElement('video');
if (dispatcher.evtVideoPlaying) {
// set our dispatcher's evtVideoPlaying
// function as the onplaying event
// callback.
video.addEventListener('playing',
dispatcher.evtVideoPlaying.bind(null, video);
}
// etc ...

Trust me when I say that inversion of control used to seem like a much more radical idea. And, while we’re at it, the partial function application that bind() does seemed much more edgy, too, in the distant past when we programmed on clay tablets and our compilers didn’t support tail call optimization. (Uh, well, hmm.)

As long as we’re creating closures (which is another way into thinking about what bind() does), we might as well add some state to the dispatcher to customize things a little more. Instead of defining our dispatcher as an object literal, we can write a function that makes the dispatcher and we can pass in arguments to that function.

Here’s an example in which we pass in a logger function, which every function inside our dispatcher can then make use of.

function createDispatcher ({ logger }) {
return {
evtVideoPlaying: (videoElement, e) => {
logger(`video element playing (src ${videoElement.src})`);
},
evtVideoPause: (videoElement, e) => { ... },
evtVideoLoadedMetada: (videoElement, e) => { ... },
};
}
let dispatcher = createDispatcher({
logger: console.log.bind(console),
});

This is really useful. It’s also about as much complexity as our simple dispatcher pattern can handle. As soon as I start thinking about juggling multiple dispatchers, or chaining dispatchers together, or doing any state management inside the dispatcher, or even writing functions inside the dispatcher that are more than a few lines long, I know it’s time to switch to a more structured approach. For example, over the past year we’ve rewritten a lot of our code so that we now use Redux actions where we once called dispatcher functions.

Code for this flavor of a dispatcher pattern tends to look pretty similar in Algol-family languages with dynamic typing and first-class functions — JavaScript, Ruby, and Perl, for example. Slightly farther afield, I’ve written dispatchers in Smalltalk and several flavors of LISP. In these languages, the syntax is quite different but the pattern works the same way.

In Java and C++, on the other hand, the static type system forces you to specialize your code much more. Several classic object-oriented design patterns cover some of the same ground. The Mediator pattern is probably closest to what I use dispatcher for most often.

from wikipedia: https://en.wikipedia.org/wiki/Mediator_pattern

A long time ago I built a dispatcher-ish thing into the first prototype of a low-level library written in C. The functionality was more complicated than any of the examples given here so far. The dispatcher kept track of a list of callbacks for each event the library cared about. The callback lists could be manipulated at runtime. (The list was traversed according to logic that was a lot of fun to figure out and specify, but doesn’t much matter for this story.)

Since there are sample code snippets above, here’s some illustrative C. :-)

typedef void (*error_handler_t)(int, int, char *, char *);
typedef char *(*t1_data_handler_t)(int, char *, char *, t1);
typedef char *(*t2_data_handler_t)(int, char *, char *, t2);
typedef char *(*t3_data_handler_t)(int, char *, char *, t3);
...
typedef struct dispatcher {
error_handler_t *error_handler_chain;
t1_data_handler_t *t1_handler_chain;
t2_data_handler_t *t2_handler_chain;
t3_data_handler_t *t3_handler_chain;
...
} dispatcher;

Everything worked pretty well. Some core library functionality was implemented using runtime introspection and the dispatcher. (For example, various data format conversions.) I had written nice unit tests that worked by plugging test functions into the dispatcher call chain lists.

We hired a very experienced systems hacker to turn my code into something much closer to feature-complete and production ready. Her first svn check-in (I told you this was a long time ago) replaced all the dispatcher logic with switch statements.

When I asked her why she’d ripped out the dispatcher architecture, she said, “yeah, that’s not how you do that.” Which made me laugh, because she was right. That’s not how you do that. But for the next few years, every time I touched that code, I thought about the dispatcher, gone but not forgotten!

What design patterns to you use — and over-use — no matter what language you’re programming in?

--

--