Vous êtes sur la page 1sur 7

Beginner's Tutorial on Understanding and

Implementing Null Object Pattern in C#


Rahul Rajat Singh, 21 Jan 2016

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.

Using the code


Now let us look into a very contrived example to understand this pattern. First let us try to
understand the problem by not using a Null Object.

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.

Hide Copy Code

public interface IShippingStrategy


{
void ScheduleShipping();
}

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).

Hide Copy Code

public class DHLShippingStrategy : IShippingStrategy


{
public void ScheduleShipping()
{
Console.WriteLine("DHL Shipping has been scheduled");
}
}

public class FedExShippingStrategy : IShippingStrategy


{
public void ScheduleShipping()
{
Console.WriteLine("FedEx Shipping has been scheduled");
}
}

public class InHouseShippingStrategy : IShippingStrategy


{
public void ScheduleShipping()
{
Console.WriteLine("In House Shipping has been scheduled");
}
}

Now lets look at our OrderProcessor class which will schedule shipping using the passed
in IShippingStrategy.

Hide Copy Code

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

static void Main(string[] args)


{
Console.WriteLine("Please select the Shipping method 1. FexEd 2. DHL 3. In House
Shipping");
string input = Console.ReadLine();

int choice = 0;
bool result = Int32.TryParse(input, out choice);

if(result == true)
{
OrderProcessor orderProcessor = new OrderProcessor();

IShippingStrategy shippingStrategy = null;

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

1. The code is messy due to all these null checks and


2. If an invalid shipping method is selected something needs to be done apart from just logging. Which
would mean that this invalid case handling logic will also come in the OrderProcessor class which
is direct violation of Single Responsibility Principle.

Welcome Null Object

So to avoid this problem, let us introduce a Null Object on our application. Lets call this
class InvalidShippingStrategy

Hide Copy Code


class InvalidShippingStrategy : IShippingStrategy
{
public void ScheduleShipping()
{
Console.WriteLine("Invalid Shipping Strategy");
}
}

Now with this null object in place, we need to modify the strategy creation code to cater to the
invalid scenario too.

Hide Shrink Copy Code

static void Main(string[] args)


{
Console.WriteLine("Please select the Shipping method 1. FexEd 2. DHL 3. In House
Shipping");
string input = Console.ReadLine();

int choice = 0;
bool result = Int32.TryParse(input, out choice);

if(result == true)
{
OrderProcessor orderProcessor = new OrderProcessor();

IShippingStrategy shippingStrategy = new InvalidShippingStrategy();

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.

Hide Copy Code

class OrderProcessor
{
internal void ProcessOrder(IShippingStrategy shippingStrategy)
{
shippingStrategy.ScheduleShipping();
}
}

This represents a very basic implementation of Null Object pattern.

Singleton should help


Now when we look at our Null Object, its behavior is same irrespective of any number of instances
we create for it i.e. It will handle the NO-VALUE-DO-NOTHING scenario. So does is really makes sense
to have multiple instances of this class. Perhaps not. So an optimization could be to make this class
singleton.

Hide Copy Code

class InvalidShippingStrategy : IShippingStrategy


{
private static readonly InvalidShippingStrategy instance = null;

static InvalidShippingStrategy()
{
instance = new InvalidShippingStrategy();
}

public static InvalidShippingStrategy Instance


{
get
{
return instance;
}
}

public void ScheduleShipping()


{
Console.WriteLine("Invalid Shipping Strategy");
}
}

we need to modify the strategy creation code to cater to the invalid scenario too so the program.cs
will now do something like:

Hide Copy Code

IShippingStrategy shippingStrategy = InvalidShippingStrategy.Instance;

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.

Vous aimerez peut-être aussi