Null Handling Strategies
I mentioned in an earlier post my dislike of the passing of null around code, and since then there have been a couple of posts on the subject on the ThoughtWorks blogs.
I had always thought that was a silver bullet for the way that we can handle null objects in our code but it seems from reading other people’s opinions and from my own experience that this is not the case (surprise, surprise!). Several ideas of how to handle nulls came out in these posts and the comments posted on them, and it seems to me that there are several strategies for handling nulls in code.
Return null
The most common way of handling cases where there is no object to return, the code just returns null instead.The problem with this approach is that the client now has to handle two different types of results. One if an object is returned and another if nothing is returned. This either results in code like this being scattered throughout the code base:
1 2 3 4 5 ~~~ |
Car car = carRepository.RetrieveCar(carId)
if(car != null)
{
car.Drive();
}~~~
|
Scala actually has an interesting way of getting around this problem by allowing you to define an interface which informs the client that there is the potential for nothing to be returned, therefore effectively designing a contract which allows the client to deal with it appropriately. The Option[T] idea is explained about half way down the page on this post.
This can also be done in C# to an extent by making use of the nullable operator. In C# it is only useful when you want to make it clear to the client that they may get a null value instead of a primitive.
Overall, this is the simplest solution and probably also the easiest to understand. It just doesn’t result in the cleanest code.
Throw Exception
The idea here is that if there is no object to return, the code throws a custom exception which describes the reason that no object was found.
1 2 3 4 5 6 7 8 9 ~~~ |
public Car RetrieveCar(Guid carId) { Car car = FindCarInDatabaseBy(carId); if(car == null) { throw new CarNotFoundException(); } return car; }~~~ |
As well as this, in theory you could end up with a method returning different Exceptions depending on the reason for the failure to return an object.
I’m not a big fan of handling state via exceptions as I think that exceptions should only be used where something exceptional has happened and I don’t think that failing to find an object can be considered exceptional in most cases.
Null Object Pattern
The null object pattern is the most helpful of the null handling strategies as far as the client is concerned. The client will now know that the object it has been returned is a null object, it will just see an object of the type requested. The devil is in the detail:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ~~~ |
public class Car { public virtual void Drive() { // Do some driving } public static Car NULL { get { return new NullCar(); } } class NullCar : Car { public override void Drive() { throw new NotImplementedException(); } } }~~~ |
1 2 3 4 5 6 7 8 9 ~~~ |
public Car RetrieveCar(Guid carId) { Car car = FindCarInDatabaseBy(carId); if(car == null) { return Car.NULL; } return car; }~~~ |
These are the main ways I have come across for handling nulls. I’m sure there are others so if you know of any better ways please let me know.
About the author
Mark Needham is a Developer Relations Engineer for Neo4j, the world's leading graph database.