The Null Object Pattern: When a slacker is just what you need

I had a challenge that was neatly solved by the Null Object pattern. I'd like to share it with you, so that I can explore the idea and provide a practical example.

Simplifying a bit, I have a Person object, and I need to fill it with details retrieved from an external system. When I started looking at the code, the call to look up the Person attributes took a Person as a passed-in parameter and modified it. That struck me as bad behavior ("Hey! I gave you that so you could use it, but I didn't expect you to change it. Sheesh."). I thought it would be more honest for the method to return information, which the controlling class could use to update the Person object if it chose to.

Let me name the players, to make this easier to follow. I have a class coordinating activities that, in our business context, is called a Translator. I created a Client that makes the actual calls to the external system. Before the refactoring, the Translator would call Client.Lookup(Person). The Client would create a message to the external system, get back a response, and use it to set attributes in the Person.

I changed Client.Lookup so that it does not change the Person, and instead returns a Response that contains the needed attributes. But if the external system did not have any info to return, should Lookup return null, throw an exception, ...?

Usually the most appropriate answer to this question is to throw an exception. If no info means you're in an invalid state or an unknown state, then it is not safe to continue, and the code should throw. In this case, though, we could continue. We didn't require the info coming back from the external system; it was just handy if available.

So I return null? But that means every time I call Client.Lookup, I have to check whether the Response is null before I use it. And so does anyone else who might be calling it in the future. It seems disingenuous for a method to say, "I'll give you a Response, but I might actually give you an empty bag. I hope you've guessed I might do that and planned accordingly."

   12 public void DoFancyBusinessSteps(Person person)

   13 {

   14     Response response = client.Lookup(person);

   15     if (response != null)

   16     {

   17         person.Address = response.Address;

   18         person.ExternalSystemId = response.ExternalSystemId;

   19     }

   20     //More stuff based on the Person...

   21 }


I'd rather return an object that is safe to use, regardless of what answer we got from the external system, and is helpful if we received useful info. This is the Null Object pattern.

I created an IResponse interface that exposes one method, Update(Person). Next I created two implementations of that interface, a Response and a NoDataResponse.

   12 public void DoFancyBusinessSteps(Person person)

   13 {

   14     IResponse response = client.Lookup(person);

   15     response.Update(person);

   16 

   17     //More stuff based on the Person...

   18 }


Response.Update uses its fields to set properties on the Person (with a method name that clearly states it is doing so). NoDataResponse.Update quietly does nothing. This allows the Translator to ask the Client to look up info about the Person, and ask the resulting Response to update the Person.

   21 public class NoDataResponse : IResponse

   22 {

   23     public void Update(Person person)

   24     {

   25     }

   26 }


I like it. As with all good tools, it's prudent not to over-use it. If quietly doing nothing would leave the Person object in a bad state, so that it blew up or corrupted data when you tried to use it later, then don't use the Null Object pattern. Throw an exception instead. The Null Object pattern is handy when you want to return an object that can do stuff in some conditions and will be harmless in other conditions.

1 comment:

Chika Buschwa said...

Thank you very much for this article. I was looking into Null Object Pattern, and I was a bit confused. How can you possibly continue execution when you don't have necessary object and instead you have only something that mimics it? Wouldn't that caused the system to behave in a wrong manner? But now, after reading the article, I realize you can use Null Reference for parts of the workflow in which is not important whether you got some piece of object or not. In my opinion, examples in literature are really really bad, often you would see that call to userRepository.GetUser(Id) can return Null Object, but in my opinion that is very wrong. What would happened if after this someone calls userRepository.SaveUser(nullObject)??

Anyway, thanks for the article, and sorry for my English, I'm not a native speaker.

Have a nice day!