A Day In The Lyf

…the lyf so short, the craft so longe to lerne

Dealing with Exceptions

We had an interesting discussion on the XP news group this past week about dealing with exceptions. The discussion seemed to focus on functions that return something rather than functions called for their side effects. Four general approaches were mentioned:

Return a special value (null or an error code)

This was generally seen as the weakest approach. Returning a null forces a null-check on each client of your method, which has the unfortunate effect of distributing complexity instead of localizing it. The canonical example is with lists—it is much better to return an empty list than a null one. Client code can work without any changes with an empty list. The same principle applies to error codes.

Throw an exception

This was the most controversial option. Several developers argued that exception handling is prone to the same weakness as the special return value, that it spreads complexity around. As Ron Jeffries’ said: “Throwing an exception doesn’t solve the problem any more than throwing up in the morning solves one’s drinking problem.”

Nevertheless, throwing an exception has proven to be the mainstream choice in modern languages, and the way exceptions bubble up the call stack can come in handy when used judiciously. Anthony Williams advocated using them, noting that they clearly signal an abortive operation (rather than relying on clients to check a return value), and allow application developers to write a global exception handler that catches any uncaught exceptions throughout the application.

The discussion intrigued me largely because I continue to use exceptions when convenient, although I’m interested in learning alternative approaches. I think that at least some of the hostility towards exceptions can be traced to the ugliness of checked exceptions (as in Java), which force clients to write try/catch blocks even if they’d prefer the exception bubble up.

Return a polymorphic value

This is probably the purest object-oriented solution, but I’m skeptical that it can work as a general case. Woolf’s Null Object Pattern is a special case—the empty list mentioned above being a simple example. We use a more sophisticated example in the content management system we developed for our website at work. When you go to ”/Content/AboutUs,” for example, the application knows “AboutUs” is supposed to be a content-managed page, and requests the content from the database. If the page doesn’t exist, rather than return null or throw an exception, it returns a new web page whose content matches our 404 page content.

If a page is content-managed, then users can edit it directly on the page in a fairly typical way. The content-managed portion is outlined with an ‘edit’ link at the top that puts the content in a WYSIWYG editor. However-this is the crucial point-the web page returned when it doesn’t exist in the database is a content-managed page. Yes, it has the 404 content, and anybody visiting that URL without content editing privileges sees what they are supposed to see. But if you do have content editing privileges, you see the familiar border with the edit link, and you can use the exact same procedure that you use to update other pages to create new pages.

The advantage to this approach is obvious—clients do not need to behave any differently regardless of whether the method call succeeded or failed. It was suggested that perhaps the same approach could be used even if some other exception occurred. I think in some cases it may be possible. I looked over some of our code for an example only to discover that the large majority of methods we use that throw exceptions have a void return type, but found an example of an exception-throwing method in our credit card processing code. The code simply sends an XML document to the company we use to process credit cards and returns a value representing the result of that transaction. It looks something like this:

public CreditCardResponse Send(string xml)
{
    IServerXMLHTTPRequest server = GetServer();
    try
    {
        server.send(message);
    }
    catch (Exception ex)
    {
        throw new ApplicationException("connection failed");
    }
    return Parse(server.responseText);
}

This code clearly could benefit from removing the thrown exception and enhancing or subclassing CreditCardResponse. That return type is already responsible for reporting errors given in the return XML by the credit card processor; why would clients want to treat failing to connect to the credit card processor any differently than one of those errors?

Callbacks

At first glance, I don’t see why a callback would be any easier than an exception handler for dealing with errors, other than perhaps making it easier to localize handling certain exceptions in one place. They have the unwanted effect of cluttering up the API by adding exception handling callback parameters.

Advertisements

Written by Brandon Byars

August 27, 2007 at 9:35 pm

Posted in Design

%d bloggers like this: