Académique Documents
Professionnel Documents
Culture Documents
Introduction
In this article we will try to understand the Null object pattern and see how this pattern can help us
to write more robust code. We will implement a contrived example to demonstrate this pattern.
Background
There is no developer in the world who has not encounters a "Null reference exception" in his
development career. What is this exception? Well this exception simply tell that we are trying to
access to deference a variable with NULL value. Which brings another question, what is a NULL
value? NULL in most programming languages indicates the absence of a value. For any variable, if
the value is NULL, it would mean that there is no value associated with that variable and thus the
caller should not perform any operation using this variable.
From a language perspective, NULL is very important and does makes sense as there are scenarios
where we want to represent the absence of value in a variable. But from the code maintainability
perspective, it would mean that whenever we are getting an object from another
module/class/service, we should always check if it is null or not before using this variable. This could
lead to more complex and unmaintainable code.
Now the Null object pattern try to ease this problem. This pattern suggests that whenever we have
dependent modules returning objects to each other, we should always return an object. The absence
of a valid value should also be represented as a special object in this case. This can be implemented
creating and returning instances of a concrete class that implements the same interface the valid
class implements. but it will do nothing whenever any operations are performed on it. What this will
do is that it make the calling code much simpler since the calling code will not have to check for null
but simply call the methods on the return object.
If we try to visualize the Null object pattern, it will look something like following.
Client. This class that requires the instance of an object to use.
DependencyBase. This is an interface that all the concrete classes(that could be returned to client)
will implement.
Dependency. This is the actual concrete classes(that could be returned to client)
NullObject. This is our Null Object class. This will be returned to the client whenever we want to
return NO-VALUE or absence of value to the client. In other words, this is the class that will be
returned to the client in all the scenarios where otherwise we would have returned null. Its important
to note that this class will implement the DependencyBase interface but will provide a DO-
NOTHING implementation for the interface since absence of value means no operations should be
possible.
Now before we move on to see how this could be implemented, let us discuss when it makes sense
to use this pattern. If we try to use this pattern for all possible classes in the system, we will end up
increasing the complexity of the system rather than reducing it.
It only makes sense to use this pattern when we have some class that is expecting another class and
is using some sort of factory, service locator or strategy to get the instance of this class. This will
ensure that whenever a dependency is being pulled from external module, we don't have to worry
about the NULL values but rather the external system will return a NULL Object which can safely be
used for do nothing type of operations.
In this example, we will ask the user to select a shipping method(yes yet another e-commerce
example). Based on the users selection, we will select the appropriate shipping strategy and pass it to
the OrderProcessor class. OrderProcessor class will use it to schedule the shipping. So lets
start by looking at our interface that represent the shipping strategy i.e. IShippingStrategy.
Once we have this interface ready, lets look at the concrete classes for shipping. The reason for these
separate classes is because each of this class will call a web service of the respective shipping service
provider to schedule a shipping(In real world, here we are just logging method call on console).
Now lets look at our OrderProcessor class which will schedule shipping using the passed
in IShippingStrategy.
class OrderProcessor
{
internal void ProcessOrder(IShippingStrategy shippingStrategy = null)
{
if (shippingStrategy != null)
{
shippingStrategy.ScheduleShipping();
}
else
{
Console.WriteLine("Invalid Shipping Strategy");
}
}
}
And finally lets look at the code that is mimicking the selection of shipping method from the user.
Hide Shrink Copy Code
int choice = 0;
bool result = Int32.TryParse(input, out choice);
if(result == true)
{
OrderProcessor orderProcessor = new OrderProcessor();
switch(choice)
{
case 1:
shippingStrategy = new FedExShippingStrategy();
break;
case 2:
shippingStrategy = new DHLShippingStrategy();
break;
case 3:
shippingStrategy = new InHouseShippingStrategy();
break;
}
orderProcessor.ProcessOrder(shippingStrategy);
}
Console.ReadLine();
}
Now the problem we have talked about the NULL values is quite evident when we look at
the OrderProcessorcode.
The order processor is checking whether the passed strategy is null or not. If it is not then some
action is being taken otherwise some different action. Now there are two issue with this
So to avoid this problem, let us introduce a Null Object on our application. Lets call this
class InvalidShippingStrategy
Now with this null object in place, we need to modify the strategy creation code to cater to the
invalid scenario too.
int choice = 0;
bool result = Int32.TryParse(input, out choice);
if(result == true)
{
OrderProcessor orderProcessor = new OrderProcessor();
switch(choice)
{
case 1:
shippingStrategy = new FedExShippingStrategy();
break;
case 2:
shippingStrategy = new DHLShippingStrategy();
break;
case 3:
shippingStrategy = new InHouseShippingStrategy();
break;
}
orderProcessor.ProcessOrder(shippingStrategy);
}
Console.ReadLine();
}
And the best part is that the OrderProcessor will not have to worry about the invalid shipping
selection at all. All the code to handle the invalid shipping method can be put inside our Null Object
i.e. InvalidShippingStrategy class.
class OrderProcessor
{
internal void ProcessOrder(IShippingStrategy shippingStrategy)
{
shippingStrategy.ScheduleShipping();
}
}
static InvalidShippingStrategy()
{
instance = new InvalidShippingStrategy();
}
we need to modify the strategy creation code to cater to the invalid scenario too so the program.cs
will now do something like:
Now we have a singleton Null object and thus only one instance of this Null object will be there in
the system and thus some performance gain.
Note: The important thing to remember about this pattern is that it should be used whenever we
see multiple classes collaborating by passing objects. Some examples for this could
be strategy pattern, factory pattern, Service
Locator, Command pattern, Repository Pattern etc. Having this pattern for all classes is not a
good idea and could lead to more complex code.
Points of interest
In this article we have looked at Null Object pattern. We have seen how this pattern can make the
calling code much cleaner with no null checks. We have also looked at a very basic(and not so real
world) implementation of the pattern. This has been written from beginner's perspective. I hope this
has been informative.