Skip to content

The Null Object Pattern

18 June 2009

The null object represents an object that does not exist. I wanted to write a post about this pattern for some time now but I just didn’t have the inspiration to come up with a good example. Fortunately, I wrote some code that needed to use just this pattern at work. Basically we had a map file which stored some additional information which was needed for reporting. Let’s call it customer map file. Thought not used by the system itself, which just uses a CustomerID, the map file contained information on the customer which had to be used in the reports. Each row in the map file contains the following tab separated values: CustomerId, CustomerName, CustomerAddress. So I wrote a class to represent a row in the file:

class MapRow
{
    private int customerId;
    private string customerName;
    private string customerAddress;

    public MapRow(string lineOfText)
    {
        // Parse a line from map file and set private members
        ...
    }

    public int CustomerId
    {
        get
        {
            return customerId;
        }
    }

    public string CustomerName
    {
        get
        {
            return customerName;
        }
    }

    public string CustomerAddress
    {
        get
        {
            return customerAddress;
        }
    }
}

Then I wrote another class to open and read the file, which wraps a dictionary and returns MapRow objects based on CustomerId:

class MapFile
{
    private Dictionary mapRows;

    public MapFile(string fileName)
    {
        // Read map file and for each row create a new MapRow then add it to the dictionary (key is CustomerId)
        ...
    }

    public MapRow this[int customerId]
    {
        get
        {
            return mapRows[customerId];
        }
    }
}

In case you are wondering, the last block of code implements an indexer on the class and can be used like:

MapFile mapFile = new MapFile("CustomerMapFile.tsv");

// Get map data for CustomerId 1
MapRow row = mapFile[1];

So, using the customerId from the system, I started generating reports by calling mapFile[customerId].CustomerName and mapFile[customerId].CustomerAddress when I realized that, for whatever reason, some customers don’t appear in the map file.

In the current implementation, the dictionary throws an exception in this case – key not found. The bad way to go about it is this:

public MapRow this[int customerId]
{
    get
    {
        if (mapRows.ContainsKey(customerId))
        {
            return mapRows[customerId];
        }

        return null;
    }
}

Why would this be the bad way? Because all code that indexes MapRow will have to check that the return object is not null. Let’s say that reporting does something like

reportStream.WriteLine("Name: {0}, Address: {1}",
    mapFile[customerId].CustomerName,
    mapFile[customerId].CustomerAddress);

Now we would have to refactor to this:

MapRow row = mapFile[customerId];

if (row != null)
{
    reportStream.WriteLine("Name: {0}, Address: {1}", row.CustomerName, row.CustomerAddress);
}
else
{
    reportStream.WriteLine("Name: Not Available, Address: Not Available");
}

And just imagine how the code gets if the map is used in multiple places. The solution is to use a null object which represents unavailable customer data. We can add it to the MapRow class as follows:

private static MapRow nullRow = new MapRow("-1\tNot Available\tNot Available");

public static MapRow NullRow
{
    get
    {
        return nullRow;
    }
}

And the indexer becomes:

public MapRow this[int customerId]
{
    get
    {
        if (mapRows.ContainsKey(customerId))
        {
            return mapRows[customerId];
        }

        return MapRow.NullRow;
    }
}

Now we can again use the simple

reportStream.WriteLine("Name: {0}, Address: {1}", row.CustomerName, row.CustomerAddress);

Whenever you find yourself checking again and again for nulls, you should consider using the null object pattern instead. Implementations of it appear throughout the .NET Framework. For example, String.Empty represents an empty string, while StreamWriter.Null represents a strem to which one can write but the written data doesn’t go anywhere.

From → code complete

Leave a Comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: