Co-Author: Rexhep Kqiku
There are many programming languages and frameworks out there, which are prepared to be used to solve problems. Although there are billions of problems that we may be asked to solve, there are usually some common themes among many of them. As we usually try to solve business problems, we also want to improve the quality of our work. This way, we have design patterns that are abstractions of some of the best ways to deal with certain situations.
Finding elements of tables by their unique identifiers is something that we do very often, for which we may need to consider implementing Null Object Pattern.
This pattern is used to deal with the absence of an object by providing an alternative that indicates this absence. In most object-oriented languages, such as Java or C#, references may not be pointing to any object at all. This way, we need to check whether we have null references, before we face major bugs after that.
Let’s take some examples and further illustrate this pattern. We will be using C# with a very simple example:
By definition, we always expect to receive a Person. In reality, we may have frequent cases when this personId is not the ID of any element in our Persons table. The go-to solutions for many developers in such cases may be:
This is not a good implementation. When we cannot find a Person, we are going to get a null value, but we are actually looking for a Person.
This null result does not match with the name of our method. If we were to have a special case when we would also want to return null, that we would have to rename the method to GetPersonByIdOrReturnNull, which would make a lot more sense for the client that calls this method.
Another problem that may happen in such cases would happen when we would have to get an attribute of the object returned, for example: person.Name. This is a famous error for .NET developers: Object reference not set to an instance of an object, or NullPointerException, if a similar syntax would be implemented in Java.
A better solution for the above scenario could be:
In this case, we have used null object pattern, which allows you to return a Person object, but that has its attribute intentionally left as empty, for example:
Another good implementation of this pattern could be the following:
In this case, we are using user-defined exceptions. We are throwing and catching specific exceptions, as a way of having better control over our application, and to also avoid NullReferenceException cases as much as possible.
Throwing our own specific exceptions can help us have more descriptive errors, which can save us a lot of time when we are trying to debug and solve problems. We can easily have a better gist of the problem that we are facing very quickly.
A slight implementation of this pattern is already included in the active ActiveRecord module in Ruby on Rails, which is an implementation of the Active Record pattern which itself is a description of an Object Relational Mapping system. Namely, when you are searching for a particular object by a primary key, you will get an exception.
The founder of Ruby on Rails, David Heinemeier Hansson explains this in the book Agile Web Development with Rails:
When you use a finder driven by primary keys, you’re looking for a particular record. You expect it to exist. A call to Person.find(5) is based on our knowledge of the people table. We want the row with an id of 5. If this call is unsuccessful — if the record with the id of 5 has been destroyed — we’re in an exceptional situation. This mandates the raising of an exception, so Rails raises RecordNotFound.
On the other hand, finders that use criteria to search are looking for a match. So, Person.find(:first, :conditions=>”name=’Dave’”) is the equivalent of telling the database (as a black box) “Give me the first person row that has the name Dave.” This exhibits a distinctly different approach to retrieval; we’re not certain up front that we’ll get a result. It’s entirely possible the result set may be empty. Thus, returning nil in the case of finders that search for one row and an empty array for finders that search for many rows is the natural, nonexceptional response.
Now that you have read this article about this pattern, remember to not let NULL make your app a fool!
Find the co-author on LinkedIn.