Home » Design Patterns » Null Object pattern – how and when to use it

Null Object pattern – how and when to use it

How often in your code do you see ifs checking whether the object is not null? Often? Very often? What would happen if you didn’t have to check it out? Surely code would be easier to maintain – no ifs = no test cases. This can all be achieved using Null Object pattern.

Null Object pattern should be used as a substitute for objects which don’t exists in the runtime or you don’t know current state of the object. You may apply it, however, bear in mind that it’s not suitable for all test scenarios. You’ve to analyze whether it’s a good idea to use it.

I recently worked on a part of code that was written years ago (sick!), there was almost no unit tests and all classes were static. My task was to add functionality that would raise an event and send it to AWS Simple Notification Service (SNS). Based on incomming SNS events subscribers would do their work – some kind of read-only data model. That’s when I got the idea to use Null Object design pattern.

The best way to solve this problem would be to rewrite everything, create proper unit/integration tests and write new functionality. However, at that moment this was not possible – it wasn’t the highest priority. I had to just write new functionality and move on. The code more or less looked like this:

public static class CruiseDb
{
    private static string ConnectionString => "ConnectionString";
 
    public static void Save(Cruise cruise)
    {
        using (var sqlConnection = new SqlConnection(ConnectionString))
        {
            // ADO.NET operations to save Cruise in database
        }
    }
}
 
public class Cruise
{
    public int CruiseId { get; set; }
 
    public string CruiseName { get; set; }
 
    public int ShipId { get; set; }
 
    public DateTime DepartureDate { get; set; }
 
    public DateTime Created { get; set; }
}

My task was to send events to SNS. In Iglu we’ve got Nuget packages for most of AWS common scenarios. I had to write a wrapper around SNS implementation and send raw objects. However, there was a problem with the way how I would pass my newly created object into static class. Dependency Injection wasn’t an option, the class was static and I couldn’t pass it via constructor. I didn’t want to do it via method parameters because I didn’t want to modify all places where this static method was used.

Singleton – first implementation

Then I found the way how I may do it. Singleton! First implementation looked like this:

/// <summary>
/// Wrapper around internal SNS common client
/// </summary>
public interface IMessagePublisher
{
    void Send<T>(T message);
}
 
/// <summary>
/// Exact IMessagePublisher implementation implementation
/// </summary>
public class MessagePublisher : IMessagePublisher
{
    private readonly Publisher _publisher;
 
    private MessagePublisher(Publisher publisher)
    {
        _publisher = publisher;
    }
 
    public static IMessagePublisher Instance { get; private set; } = new MessagePublisher(new Publisher());
 
    public void Send<T>(T message)
    {
        // Do additional operations on the message
 
        _publisher.Send(message);
    } 
}
 
public static class CruiseDb
{
    private static string ConnectionString => "ConnectionString";
 
    public static void Save(Cruise cruise)
    {
        using (var sqlConnection = new SqlConnection(ConnectionString))
        {
            // ADO.NET operations to save Cruise in database
 
            // Publish an event
            MessagePublisher.Instance.Send(cruise);
        }
    }
}

Publisher is the previously mentioned internal SNS implementation. IMessagePublisher interface is just a wrapper for this class so I may use easier way to implement it in a static code. MessagePublisher is an interface implementation with Singleton design pattern. I’ll skip all thread safety considerations here just to make this subject easier to understand.

Everything looked great until I fired the existing tests for CruiseDb static class. Several bugs in many places (other static classes have been using it in many test cases). I didn’t want to fix all test cases, it’s not the way how SOLID works. Boom!

Null Object pattern to help!

Here comes the Null Object pattern. I just simply replaced IMessagePublisher implementation with a class which did nothing. I had, however, to remember to initialize it once the program starts (in this case, the web application). My code looked something like this:

/// <summary>
/// Exact IMessagePublisher implementation
/// </summary>
public class MessagePublisher : IMessagePublisher
{
    private readonly Publisher _publisher;
 
    public static void Init() => Instance = new MessagePublisher(new Publisher());
 
    private MessagePublisher(Publisher publisher)
    {
        _publisher = publisher;
    }
 
    public static IMessagePublisher Instance { get; private set; } = new NullMessagePublisher();
 
    public void Send<T>(T message)
    {
        // Do additional operations on the message
 
        _publisher.Send(message);
    } 
}
 
/// <summary>
/// IMessagePublisher implementation which does nothing
/// </summary>
public class NullMessagePublisher : IMessagePublisher
{
    public void Send<T>(T message)
    {
        // Do nothing
    }
}

By default, Singleton will use Null Object which does nothing. In the worst case scenario an event won’t be raised and we’ll recognize it during our internal testing. The only thing we’ve to remember during implementation is to fire the Init() method once the application starts.

In the case I would like to pass a different IMessagePublisher implementation into singleton, I could just overload Init() method passing exact interface implementation and replace default MessagePublisher instance. Code would look like this:

/// <summary>
/// Exact IMessagePublisher implementation
/// </summary>
public class MessagePublisher : IMessagePublisher
{
    private readonly Publisher _publisher;
 
    public static void Init() => Instance = new MessagePublisher(new Publisher());
 
    public static void Init(IMessagePublisher messagePublisher) =>
        Instance = messagePublisher;
 
    private MessagePublisher(Publisher publisher)
    {
        _publisher = publisher;
    }
 
    public static IMessagePublisher Instance { get; private set; } = new NullMessagePublisher();
 
    public void Send<T>(T message)
    {
        // Do additional operations on the message
 
        _publisher.Send(message);
    } 
}

Summary

Null Object pattern is a very useful design pattern. Once properly implemented, it is not necessary to check whether the object is null or not. The only downside to the use of this pattern is the fact that we must remember that our operation simply will not be executed in some cases. In my case this is not happening. This class was used only in one application, however in many places. One call to Init method would do the job.

Published by

Mateusz Pustelak

Technical Lead at Iglu.com, Software Developer with several years of commercial experience, TDD practitioner, DDD/CQRS fan.

Leave a Reply

Your email address will not be published. Required fields are marked *

*