ASP.NET Hosting

Null Object design pattern instead of returning null

This is just a quick post to make a plug for the Null Object design pattern. I don't think that it's used a lot, but it's useful to avoid a lot of issues.

It's very common to create or use methods and properties that return null. That's not surprising. It's part of most of the programming languages we use nowadays. However, returning null can lead to issues because if the callers forget to test for null, it will likely provoke a NullReferenceException (or equivalent).

What the Null Object design pattern suggests is to always return a valid instance instead of null. Where you used to return null, you would return an object with a "null" or default behavior/state.

Example:

customer = GetCustomer();
plan = (customer != null) ? customer.Plan : BillingPlan.Basic;

can be replaced with

plan = GetCustomer().Plan

if GetCustomer never returns null, but instead an instance of NullCustomer (or DefaultCustomer), which is a sub-class of the Customer class and whose Plan property is defined as follows:

override BillingPlan Plan
{
  get { return BillingPlan.Basic; }
}

This is the same approach as returning an empty array instead of null. When you return null, the callers need to check for null. When you return an empty array, the callers can try to iterate it without a problem - even if they forget to check for null.

Compare the following methods for example:

Customer[] GetCustomers()
{
  if (!Initialized)
    return null;

  ... // return data
}

and

Customer[] GetCustomers()
{
  if (!Initialized)
    return new Customer[0];

  ... // return data
}


Here are two pointers to learn more about this pattern:

Keep in mind that this is just a pattern, and like the other ones it's not to be used blindly in all cases. Returning null is of course perfectly fine where it makes sense.

6 Comments

  • So if you return some default value instead of null, wouldn't you have to in many cases still have to check for the default value to make sure you have a valid object?  I don't see the difference between checking for a default value vs. null.

  • Dukebaby, the point is to not have to check anything. With this pattern, all the returned objects have a behavior and data. The "null" objects have their own too.
    Let's say call a method that returns a Tool. Sometimes, the result will be a Hammer, sometimes a Wrench, or a ScrewDriver, etc. When you call the Use method on each of these tools, you get a different result.
    Now, if the method returns a NullTool, calling the Use method would result in no action done at all. All this without having to test for null before the call to Use() and everywhere else when you pass the Tool around in the code.

  • Dukebaby, a useful scenario would be something like a null cache instance. That way you can write everything assuming there's a cache, but the null cache would never store anything.

    They're also used in game programming a lot, where there's usually a null controller being checked if you're not using a gamepad.

  • I call it the "Empty" pattern, as in string.Empty (from the .Net Framework) from which I discovered this pattern.
    The code is a way more simple and readable when you can call one of an object's members without having to check whether if the object is null.

  • Good call, the only thing I would say is that if you're checking for !Initialized you should probably be throwing an exception instead of returning null or a default value. Since that would be an error state.

    Also, you probably shouldn't be returning arrays unless you intend to allow the consumer to replace values in the array. It's a lot better to use IEnumerable for your return value and do a yield break.

  • Agreed, !Initialized is probably not a good example.

    Not so sure about IEnumerable. It all depends on what you want. In some cases, you'll want the stream approach proposed by yield break. In other cases, you'll want a definite set of items (what a cache would return, for example) on which you can call Count without any enumeration taking place, you can access elements via their indexes, and no deferred execution (thinking about LINQ here).

Comments have been disabled for this content.