Monday, 28 February, 2005

I'm currently re-reading Martin Fowler's "Refactoring" and I saw an interesting design pattern in there.

It's called the Null Object and it's a really, really cool idea. You see most of the bug in our code base at work come from code that doesn't exist; that is, failure to test if the object we've requested returned null or not. Null objects obliterate this type of bug by effectively creating a "do nothing" class.

Here's how it works. Imagine we have an Account and Order class, there will be various properties and methods associated with both of these objects but for the sake of this discussion let's just distill it down to a couple of simple properties and methods.

  1. On the Account class there is a static method called retrieve that accepts a single parameter, the AccountId, and returns an instance of the Account class pertaining to the account with that id.
  2. On the Account class there is an instance method called "GetLastOrder" which accepts no parameters and returns an instance of the Order class pertaining to the last order that account made. If the Account in question has made no orders then a NULL value is returned.
  3. On the Order class there is a static "Retrieve" method that accepts a single parameter, the Order Id, and returns an instance of the Order class which pertains to that Order Id.
  4. There is a Refund instance method on the Order class that will refund the entire value of the Order to the credit-card that placed the order. It takes no parameters and returns a boolean signaling the success or failure of the refund.

With this in mind consider the following:

   private void RandomMethod()
   {
      Account someBodysAccount = Account.Retrieve(100);
      Order someBodysLastOrder = someBodysAccount.GetLastOrder();
 
      // Refund the Order
      someBodysLastOrder.Refund();      
   }

Spot the potential for the bug? If GetLastOrder returns a null the code will explode when you try to invoke the Refund method. The standard way of dealing with this is to check that the Order isn't null before invoking the Refund method but programmers have bad memories and some of these checks invariably get missed. Null Objects are sexy because they allow you to demolish these types of bug with greatest of ease.

With Null objects, you modify the program so that instead of GetLastOrder returning NULL if there's no order it returns a special version NULL version of the Order class. All the properties and methods are the same but the object itself doesn't do anything. In our case, the Refund method wouldn't do a thing: it'd consist of a simple return true. That way, if there is no last order the code still proceeds fine without the need for checking for nulls everywhere.

The same goes for the Account class too. We can have a NULL version of that class. Suppose that an account with the ID of one hundred didn't exist. The Retrieve method could return a NULL Account class. When the GetLastOrder method is invoked against that instance NULL Account instance it returns a NULL order object. Code execution then proceeds to the Refund line and that method simply returns true. I now give permission for you to cry for a moment or two, with excitement, as you ponder the number of if statements you can cull from your program using this technique.

Of course, like any technique it has to be used wisely. You have to be careful not to report success on methods that change the state of the system where in fact no change of state took place. In our example, I said our Refund method on a NULL Order object returned true - perhaps a more appropriate course of action would have been to throw a custom exception like "RefundAttemptedFromNullOrderException"

Even so, throwing exceptions like this is a far more informative than default c-sharp "Object reference not set to an instance of an object" exception. Plus, you can stick this exception in your NULL order class and you'll catch that error one hundred percent of the time regardless of the piece of code that attempts to do a refund.

I think all in all this is a powerful technique that has potential to remove a lot of boiler-plate code. I, for one, welcome our new NULL Object overlords! Emoticon: Green Smile

Simon.

21:31:39 GMT | #Programming | Permalink
XML View Previous Posts