The Non-Polymorphic Null Object Anti-Pattern

I’ve noticed a common (anti) pattern that a couple of unrelated teams I’ve worked with have settled into, which I will explain in detail.

A common pattern to prevent null reference checks littering one’s code is the Null Object Pattern. The Null Object Pattern is an abstraction created to stand in for some concrete object type to replace the non-existence of instances of that object, so that reference to that object (or, to its abstraction) will not fail with a NullReferenceException.

This seems like an excellent idea to avoid rampant null checks as one often sees in languages which allow null values. However, I have seen multiple teams turn null check-ridden code like this:

if (item != null && item.Child != null && item.Child.Id == someId)
{
    // do some stuff with the item
}

Into code that looks like this:

if (!(item is NullItem) && !(item.Child is NullChild) && item.Child.Id == someId)
{
    // do some stuff with the item
}

What was gained here? Not much. Some extra time was spent creating new classes NullItem and NullChild, but the code is just as cluttered as before.

What’s worse, if null checks (or more precisely, null pattern type checks) were missed elsewhere (and they always are), this might push potential errors (or, unanticipated null conditions) from being run-time errors, which blow up and notify you that something unexpected occurred, to being logic errors, which allow the application to continue functioning, but in an unknown state, which is almost always much worse.

According to Robert Martin, “We can address these issues by using the NULL OBJECT pattern. This pattern often eliminates the need to check for null, and it can help to simplify the code” (Emphasis mine)

Another good reinforcing quote from sourcemaking.com:

The essence of polymorphism is that instead of asking an object what type it is and then invoking some behavior based on the answer, you just invoke the behavior. The object, depending on its type, does the right thing.

In other words, you want your Null Object to be a proper stand in for your concrete object which might otherwise be null. You don’t want you client code be aware of its existence.

Consider the following small code snippet, modelled after the way an absurdly simplified Microsoft Word-like application might function:

public interface IPersistence
{
    IDocument LoadDocument(int id);
}

public interface IDocument
{
    bool IsDirty { get; }
}

public class UserDocument : IDocument
{
    public bool IsDirty { get; private set; }
}

public class NullDocument : IDocument
{
    public bool IsDirty {
        get { return false; }
    }
}

public class Application
{
    private readonly IPersistence _persistence;
    private IDocument _currentDocument;

    public Application(IPersistence p)
    {
        _persistence = p;
    }


    public void LoadDocument()
    {
        // Returns null if document 123 does not exist.
        _currentDocument = _persistence.LoadDocument(123) ?? new NullDocument();
    }

    public bool SaveButtonEnabled
    {
        // This is one interaction of what would likely be dozens or more with 
        // _currentDocument in a real scenario, and the code in these interactions
        // does not need to be concerned with whether it is null, nor whether it
        // is of type NullDocument:
        get { return _currentDocument.IsDirty; }
    }
}

In a real project, the Application class would likely interact with many members of _currentDocument, beyond just the .IsDirty property demonstrated in the snippet. So we can choose whether to check if _currentDocument is null or of type NullDocument in each of these cases (both of which I have seen done many, many times before), or we could choose to use a properly polymorphic null object that simply stands in for the cases in which an actual UserDocument is unavailable.

The big advantage of this approach is that you can push some of the correctness checking of your code from run-time to compile-time. A future coder might neglect to check whether your potentially null object is null (or whether it is of type NullThing) in some instance, but if you add a new member to the interface being derived from (or a new abstract member to an abstract class being derived from), the compiler will enforce that the member be implemented in all derived classes.

This is really just a variation on the replace conditional with polymorphism theme, and can gain you many of the same benefits as from replacing extensive and duplicate enum switch statements with polymorphism.

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