flow.
"Flow is a condition of deep, nearly meditative involvement." - Tom DeMarco

The Null Object Pattern

Wednesday, August 01, 2007 10:28 PM
I'm currently reading through Michael Feather's Working Effectively with Legacy Code.  I had skimmed it sometime ago but as I'm currently trying to wedge unit tests around some 6 month old code that was written with testing as an afterthought I've been giving it a bit more serious glance.

If you've never read this book I really really recommend it.  Don't let the title fool you, this book is jammed pack with tons of tips that are invaluable for writing greenfield code as well...if anything, the lessons you learn here will keep you out of trouble in the future.  Also, regardless of what the Ph.D. sounding title and stagnant cover picture would lead you to believe, this is actually a very light hearted book that reads quickly with a nice little sense of humor.  Feathers, in fact, has a very enjoyable writing style that may be quite underrepresented by the first impressions of his book.

Anyway, one such gem that has came out of this book is the Null Object Pattern.  Although I had never seen this before, this is a really slick little pattern....

Imagine that you're retrieving and displaying records from a database based upon a set of IDs.  The records corresponding to your IDs may or may not exist in the database.  In fact, it's so common that your record doesn't exist that your retrieval method doesn't even throw an exception...it simply returns null.  This means that you have to check for null every time you do anything with that returned value.  This leads to a lot of ugly code bloat.

Now, imagine that you could safely call methods on a null object without any risk of a NullReferenceException or a nasty side effect.  This is what the Null Object Pattern is all about.

Here is our business object class, the Account object...

    /// <summary>

    /// Represents a live account.

    /// </summary>

    public class Account

    {

        private readonly string _id;

 

        /// <summary>

        /// Initializes a new instance of the <see cref="Account"/> class with the given ID.

        /// </summary>

        /// <param name="id">The ID of the new account.</param>

        public Account(string id)

        {

            _id = id;

        }

 

        /// <summary>

        /// Gets the ID of the account.

        /// </summary>

        /// <value>The ID of the account.</value>

        public string ID

        {

            get { return _id; }  

        }

 

        /// <summary>

        /// Processes this account fulfilling any unfilled orders.

        /// </summary>

        public virtual void Process()

        {

            ConnectToOrderService();

            FulfillAllOrders();

        }


    }



And this is what our retrieval method looks like...

        /// <summary>

        /// Gets the account which corresponds to the given ID.  If no account is found

        /// <c>null</c> is returned.

        /// </summary>

        /// <param name="id">The ID of the desired account.</param>

        /// <returns>The desired account or <c>null</c> if no matching account is found.</returns>

        public Account GetAccount(string id)

        {

            return _accounts.Find(

                delegate(Account account)

                {

                    return (account.ID == id);

                });

        }



And our consumer method...

           foreach (string id in ids)

            {

                Account account = service.GetAccount(id);

                if (account != null)

                {

                    account.Process();

                }

            }



Now, let's think about this.  What if we had a new object that was still an Account object at its base, but was perfectly safe to call this empty methods on.  Meet the Null Object...

    /// <summary>

    /// Represents a dummy account that can perform no action.

    /// </summary>

    public class NullAccount : Account

    {

        /// <summary>

        /// Initializes a new instance of the <see cref="NullAccount"/> class.

        /// </summary>

        /// <param name="id">The ID of the new account.</param>

        public NullAccount(string id) : base(id)

        {

        }

 

        /// <summary>

        /// Performs no action as this is a null account.

        /// </summary>

        public void Process()

        {

            // These accounts have nothing to process

        }

    }



Note that this is still, at its base, an Account object.  Do you spot the key difference though?  All of the business methods on this object are empty.  This means, that we can safely call them without any risk of runtime errors or side effects.  Now that we know this, lets take another look at our retrieval method...

        /// <summary>

        /// Gets the account which corresponds to the given ID.  If no account is found

        /// an instance of <see cref="NullAccount"/> is returned.

        /// </summary>

        /// <param name="id">The ID of the desired account.</param>

        /// <returns>The desired account or an instance of <see cref="NullAccount"/> if no

        /// matching account is found.</returns>

        public Account GetAccount(string id)

        {

            Account theAccount = _accounts.Find(

                delegate(Account account)

                {

                    return (account.ID == id);

                });

 

            if (theAccount != null)

            {

                return theAccount;

            }

            else

            {

                return new NullAccount("");

            }

        }



And our consumer method...

            foreach (string id in ids)

            {

                Account account = service.GetAccount(id);

                account.Process();

            }



All that had to change for our retriever was that we decided to return a NullAccount object when no matching account is found instead of returning null.  And, this simple change, has completely alleviated our consumer's need to check for null each time before doing anything.  This leads to much cleaner code on the side of our consumer.

Now, although this works great in the example above, as with any pattern there are obviously some situations where its usage is not appropriate.  For example, if you were keeping track of the number of valid Accounts in your data store by ticking a counter variable, this would not be a wise choice.  Also, any situation in which you're relying on exceptions to fail fast would be clouded by this as well. 

As with any pattern, full understanding of it means knowing not only when to use it as well as when not to use it.



kick it on DotNetKicks.com

Feedback

# re: The Null Object Pattern

"...I'm currently trying to wedge unit tests around some 6 month old code that was written with testing as an afterthought..."

I feel your pain, I'm there myself. Might just have to get me that book. Great post Jeremy! 8/6/2007 8:21 PM | Jim Burger

# re: The Null Object Pattern

Thanks Jim!

I completely recommend the book, it's full of absolutely brilliant ideas for breaking up legacy code.

Jeremy 8/6/2007 9:02 PM | Jeremy

# re: The Null Object Pattern

Yeah I like this pattern. I used it a lot in my game engine (http://sf.net/projects/jasel) for input devices. It has a lot of abstractions for input like a player x OverallInput that composes all possible inputs for player x (a part of the keyboard and a gamepad for example). In some cases, the compositions ask for inputs that don't exist (say player y should be his part of the keyboard and the yth gamepad, but y is greater than the number of gamepads installed). So in those cases I just return a NullInput object that does nothing. So I don't have to worry about special cases, and I don't have to throw exceptions because these aren't really "errors" per se. Really made managing the input system a LOT easier. 8/7/2007 8:58 AM | Matt

# re: The Null Object Pattern

Nice. A couple of small tweaks you could do.

1. The Process method in NullAccount overrides the base class so either mark it as virtual or use the new keyword.

2. You can use the null coalescing operator (if you're writing this in C# 2.0) in your GetAccount method:

Account accountToFind = _accounts.Find(
delegate(Account account) { return (account.ID == id); });
return accountToFind ?? new NullAccount(id); 8/7/2007 8:59 AM | Bil Simser

# re: The Null Object Pattern

Bil,

Thanks for the catch mentioned in number 1. Small oops on my part, I'm correcting it now. Also, that's a great idea with the ??, I hadn't even thought about that but it really cleans up the code.

Thanks!
Bil 8/7/2007 1:18 PM | Jeremy

# re: The Null Object Pattern

Thanks Matt, that's a good idea about the controller. I've been trying to think of a real good example of a 'high failure rate' situation where this would be good. That works perfect!

Jeremy 8/7/2007 1:20 PM | Jeremy

# re: The Null Object Pattern

And waht about a generic Null object? (in PHP):
class NULLObject
{
private function __call($metohd, $params) { return NULL; }
private function __get($varname) { return NULL; }
private function __set($varname, $value) { return NULL; }
private function __isset($varname) { return NULL; }
private function __unset($varname) { return NULL; }
} 8/8/2007 10:54 AM | Lucas Araújo

# re: The Null Object Pattern

Lucas,

Great comment. I've often found myself wishing that C# had a some sort of a null object built right into the framework like PHP and other languages have.

I'm really glad that you joined the discussion actually, many of the readers of this blog are (like me) C# guys so its easy to focus our thoughts on whats available in C# only and miss a lot of great solutions that are available in other languages.

Thanks for the idea!

Jeremy 8/8/2007 1:37 PM | Jeremy

 re: The Null Object Pattern

I don't see the point of this. Why didn't use Try/Catch Exceptions? I think is more readable this way.
8/8/2007 4:01 PM | Brutuscat

 re: The Null Object Pattern

Please tell me, why would you ever be interested in thinking, execution went well, if nothing happend? This seems like someone has completely forgot the benefits and purposes of OO! 8/8/2007 7:04 PM | Peter

 re: The Null Object Pattern

Account is a good example of why this is often a bad pattern.

Consider:

Account acc = getAccount(...);
acc.depositCheque(100.0);

oh dear! where did my money go? And why wasn't I told that I miss-keyed my account number?
8/9/2007 5:19 AM | Greg Allen

 re: The Null Object Pattern

.NET has it's own nullable generic object. You can declare a nullable variable two different ways:

Nullable<int> i = null;

int? i = null; // C# 2.0

You can use it like this:

if (i == null)
{
// do something
}
else
{
i = 0;
int j = i.Value;
}
8/9/2007 7:14 AM | Antão Almada

Post a comment





 

Please add 7 and 3 and type the answer here: