Sunday, July 20, 2008

The "swallowtail"-pattern

Every now and then I use a pattern that I've myself named the "swallowtail"-pattern, this pattern is particularly useful in ECMA-script but it can be used in any language that has first class functions. The is useful when a function contains conditional logic that only has to be evaluated the first time it's invoked, for example an ECMA-script function might do different things depending on the browser that's hosting the script, let's look at an example.

Browser =
{
    setInnerText: function(element, text)
    {
        if (document.all)
        {
            element.innerText = text;
        }
        else
        {
            element.textContent = text;
        }
    }
}

The "Browser.setInnerText"-function checks whether the current browser is IE and in that case uses the "innerText"-property of the html-element, if it's another browser the "textContent"-property will be used. The problem is that the if-statement is evaluated every time the function is called, but once we know that we're running either browser we know what to do. To solve this we can exploit ECMA-scripts dynamic nature and the fact that it has fist class functions. Let's rewrite the function to the following.

Browser =
{
    setInnerText: function(element, text)
    {
        if (document.all)
        {
            Browser.setInnerText = function(e, t)
            {
                e.innerText = t;
            };
        }
        else
        {
            Browser.setInnerText = function(e, t)
            {
                e.textContent = t;
            };
        }
        
        Browser.setInnerText(element, text);
    }
}

Now once the if-statement is evaluated for the first time we create a new function that sets the inner text of the element using the correct property for the running browser, this new method has no conditional logic at all, it just sets the text. We point the "Browser.setInnerText"-function to this new function instead of the old one and then finally we call it with the passed in arguments. Now, the next time the "Browser.setInnerText"-function is called it's the new faster trimmed down method instead of the one with the if-statement.

As I said this pattern can be used in any language that supports first class function, it's however not a pattern that should be over used since the overhead in the particular language might be greater than the performance benefits.