Paired methods (start*, stop*) are a bad idea

By paired methods, I mean a pattern like this:

manager.startWatching(node);
doSomething();
manager.stopWatching(node);

In pure JS land, this is unnecessary. If your API requires both the start and stop methods execute, around user code, your API is fragile. You might require customers to call both startWatching() and stopWatching(), but you can’t guarantee they’ll do it. It’s far better to have a single watcher method which takes a callback function:

manager.watchObject(node, function() {
doSomething();
});

The watchObject() can guarantee both the start and the stop methods execute, even in the face of an exception from the callback, with a try… finally statement:

watchObject: function(obj, callback) {
innerManager.startWatching(obj);
try {
callback();
}
finally {
innerManager.stopWatching(obj);
}
}

This blog post isn’t a rant against any Mozilla code in use, specifically. This is something I realized when working on my own pet project.

Though to be fair, a lot of XPCOM code requires this pattern – SAX, TransactionManager’s batched transactions… it’s kind of annoying. The only current solution for this would be to introduce a callback interface, with a single method and “function” defined in the interface attributes:

[scriptable, function, uuid(...)]
interface nsICallback : nsISupports {
void callback();
};

Technically, you could reuse nsIRunnable for that, but that violates the intent of the interface. Somehow, I don’t think we’re going to see that pattern introduced into the Mozilla code base anytime soon, even as a new XPIDL argument type. (“lambda”, bsmedberg?)

UPDATE: John J. Barton raised an excellent point in comments: that if the start method and the stop method are from two separate user interface events, such as a start button and a stop button, then it’s appropriate to have two separate methods. I wrote this article in the context of a single event triggering both.

3 thoughts on “Paired methods (start*, stop*) are a bad idea”

  1. Yes, I’m rather fond of that idiom in Python as well (albeit with special block syntax rather than callbacks). It’s a much neater way to deal with opening and closing files or database connections than the try/catch/finally blocks used in Java

  2. That pattern gets used extensively in functional programming languages like Haskell: withFoo doThis.

  3. How can you use a callback when the startWatching is triggered by one user interface event (button) and stopWatching is triggered by another event (another button)? In my experience, that is where this kind of API comes in.
    (From Alex: You raise a fair point, and I’ll update the blog entry to reflect that.)

Comments are closed.