Monday, December 2, 2013

Implicit Callbacks

While working in C# this morning, I found myself wanting to force the creator of an object to subscribe to an event. I had something like the following:
var obj = new MyObject();
obj.RequiredEvent += EventHandler;
My first solution was to extract a sort of factory method and use that whenever I wanted to construct the object:
private MyObject CreateObject()
{
  var obj = new MyObject();
  obj.RequiredEvent += EventHandler;

  return obj;
}
This is good, but it's still possible to construct the object without calling the helper method. What I really wanted was for the compiler to enforce that someone subscribe to the event, similar to how using constructor injection ensures that an object is passed all of its dependencies.

Callbacks


With this thought in mind, it occurred to me that if I really want to require subscribing to an event, maybe an event is not the right approach. What if the constructor simply took a callback function as a parameter, as follows?
var obj = new MyObject(EventHandler);
In effect, we have taken an implicit callback and made it explicit. Indeed, this is simpler and gives the compile-time check that I was after.

Pick Your Poison


As always, it's a trade-off. The callback approach limits me to one handler, whereas an event may have multiple subscribers. Furthermore, the callback approach forces the consumer to provide a handler, whereas with the event approach it is entirely optional.

Impact of Language


The language you're using may make one approach more natural than the other. In C#, I usually reach for events. In JavaScript, I'm more likely to use a callback handler. This is mainly due to the fact that C# has language support for events, while JavaScript does not. Passing around functions is also more idiomatic in JavaScript, so callbacks are a good fit.

In fact, were it not for my exposure to more functional languages like JavaScript, I probably wouldn't have thought of using the callback pattern at all. Yet another reason to learn more languages.

2 comments:

  1. There's no reason you can't force your client to supply a callback to the constructor and provide access to an event handler through some other mechanism if you want to allow them to subscribe multiple event listeners.
    In C# the Task generic class provides an easy way to pass around functions as parameters (so long as it's okay that they must return void).

    ReplyDelete
    Replies
    1. Great point Sam. Funcs, Tasks, Actions, et al do make passing around functions in C# bearable, but this is one area where I think functional languages (especially dynamic ones) really shine.

      Delete