There is a pattern present to handle the null object in programming. Want to know more about the pattern? Read this article to fully understand the null object design pattern with C# code example.
Or are you annoyed of the most frustrating exception of the programming i.e. “NullReferenceException – Object reference not set to an instance of object”.
Need for Null Object Design Pattern
“I call it my billion-dollar mistake to invent the null reference in 1965” – Sir Hoare
Above are the words of a very famous computer scientist who discovered null references. And he says that whatever language he was working on in 1965 was handling the null reference and it has been applied to all the languages which are born out of it.
The nulls are very common exception I also get while my code is in production. and I admit that I miss a number of times to handle the null reference at many unexpected scenarios which creates a lot of mess at later stages of development life cycle.
Even if we handle null reference, the code becomes a lot cumbersome to read and maintain.
Lets see an example where a null reference can cause problem.
public class Student { private string firstName; private string lastName; public string FirstName { get { return firstName; } set { firstName = value; } } public string LastName { get { return lastName; } set { lastName = value; } } public override string FullName() { return FirstName + " " + LastName; } }
Above is a Student class which contains two data member variables and method to get the full name.
Now suppose If I have a Student Repository class which contains collection of all the students and returns a particular student when we search it by name.
public class StudentRepository { static IListstudentRep = GetStudentsFromDataSource(); private static IList GetStudentsFromDataSource() { //gets the students from the datasource. return new List (); } public static Student GetStudentByFirstName(string firstName) { return studentRep.Where(item => item.FirstName == firstName).ElementAtOrDefault(0); } }
Now I am using the above repository in my client code to find and get particular student from the collection.
static void Main(string[] args) { Student foundStudent = StudentRepository.GetStudentByFirstName("Vikram"); foundStudent.FullName(); }
Suppose the student I am finding here is not available, in that case the foundStudent variable will be set to null and we will get the NullReferenceException as shown in the below figure.
NullReferenceException states that we are trying to access a place in the memory which has not been assigned.
One of the resolution for the above error can be to check for null references at each and every place where ever we have chance of getting exception.
Student student = StudentRepository.GetStudentByFirstName("Vikram"); if(student != null) student.FullName();
But the above solution will create bloated code and lots of duplication.
The other way to prevent out this error is to use the NULL Reference design pattern discussed next.
The UML for Null Pattern
Implementation of Null Object Pattern
As you can see in the figure above, I will be having an abstract class for the Student class named AbstractStudent. Please check the code below
public abstract class AbstractStudent { public abstract string FirstName { get; set; } public abstract string LastName { get; set; } public abstract string FullName(); public static readonly NullStudent Null = NullStudentInst; private static NullStudent NullStudentInst { get { return new NullStudent(); } } public class NullStudent : AbstractStudent { public override string FirstName { get; set; } public override string LastName { get; set; } public override string FullName() { return string.Empty; } } }
In the above code I have a NullStudent object which is contained as internal class by AbstractStudent. I will create a single instance of the NullStudent using singleton pattern.
The reason for having a singleton is that the behaviour and state of null object does not change.
Do nothing code is centralised using the singleton as we can see in above code, I can change the FullName() method for NullStudent and it will be prevailed across the application.
Now please have a look at the below class code for Repository class and an extension method for returning the null class.
public class StudentRepository { static IListstudentRep = GetStudentsFromDataSource(); private static IList GetStudentsFromDataSource() { //gets the students from the datasource. return new List (); } public static AbstractStudent GetStudentByFirstName(string firstName) { return studentRep.Where(item => item.FirstName == firstName).ElementAtOrDefault(0).GetNull(); } } public static class Extensions { public static AbstractStudent GetNull(this AbstractStudent student) { return student == null ? AbstractStudent.Null : student; } }
And in fact my Student class need to derive from AbstractStudent.
So in my client class there is no need for checking the null reference as shown in the below code.
AbstractStudent student = StudentRepository.GetStudentByFirstName("Vikram"); student.FullName();
Achievements using null reference pattern
- Getting rid of the null reference checks code scattered around and having more cleaner code
- Having a non functional object in place of null reference.
- Allowing methods to be called on null objects.
- Complexity of the client side goes down.
Though it is not possible to have this pattern incorporated at all the places instead of null references. But if we want to abstract out the handling of null reference out of client code and when two objects are collaborating with each other as in case of Strategy pattern, this is a must have pattern.
And definitely developers working in the client side need to be aware of the presence of null object. Otherwise they will still be doing the null reference checks in the client side.
Lucas Tagliani Aguiar says
Really nice and easy to understand article, but – as you said in the conclusion – is not possible to use this pattern in all places of a large application. However, it’s very useful in some cases (mostly in classes of lot of use and search like ‘product’. Congrats!
Vikram Chaudhary says
Yeah this is a very important pattern and can be highly useful for classes which have both state and behaviour. And it is of not much use for the class which only contains state(data)