Null Object Design Pattern in C++


Reading time: 20 minutes

The Null object pattern is a design pattern that handles null references. Null objects can arise in a program and it is usually handled as a special case which is unnecessarily adds a test case. A better way to handle this is to add native support for null objects in the class design.

It focuses on simplifying the use of dependencies. The given operation is achieved by using instances of a concrete class that further implements a known interface, instead of null references.

Key ideas/ terms:

  • Collaborators

Collaborators are classes which are entry point for a module in an application.

  • It represents the whole behavior of the module

  • It hides the implementation details.

  • each collabrator in independent and cannot access other collabarators resources

  • A client is an instance that requires a collaborator.

  • Abstract object is an object which declares the interface for Client's collaborator. It implements default behavior for the interface common to all classes

  • RealObject defines a concrete subclass of AbstractObject whose instances provide useful behavior that Client expects.

  • NullObject is an object that provides an interface identical to AbstractObject's so that a null object can be substituted for a real object.

  • NullObject does not perform any operation

  • We can have multiple NullObjects.

When to use the Null Object pattern?

  • An object requires a collaborator. The Null Object pattern does not introduce this collaboration. It makes use of a collaboration that already exists
  • Some collaborator instances should do nothing
  • You want to abstract the handling of null away from the client

Structure

topic of image

Example

A language with statically typed references to objects illustrates how the null object becomes a more complicated pattern:

class animal 
{
public:
  virtual void make_sound() const = 0;
};

class dog : public animal 
{
  virtual void make_sound() const override 
  { 
      std::cout << "woof!" << std::endl; 
  }
};

class null_animal : public animal 
{
  virtual void make_sound() const override { }
};

Here, the idea is that there are situations where a pointer or reference to an animal object is required, but there is no appropriate object available.

A null reference is impossible in standard-conforming C++. A null animal * pointer is possible, and could be useful as a place-holder, but may not be used for direct dispatch:

        a->make_sound() is undefined behavior if a is a null pointer.

The null object pattern solves this problem by providing a special null_animal class which can be instantiated bound to an animal pointer or reference.

The special null class must be created for each class hierarchy that is to have a null object, since a null_animal is of no use when what is needed is a null object with regard to some widget base class that is not related to the animal hierarchy.

Note : NOT having a null class at all is an important feature, in contrast to languages where "anything is a reference".
In C++, the design of a function or method may explicitly state whether null is allowed or not.

// function which requires an animal instance,
// and will not accept null
void do_something( const animal& Inst ) 
{
  // Inst may never be null here
}

// function which may accept an animal instance or null
void do_something( const animal* pInst )
{
  // pInst may be null
}

What are the Advantages and Disadvantages of Null Object Design Pattern?

Advantages:

  • It defines class hierarchies consisting of real objects and null objects. Null objects can be used in place of real objects when the object is expected to do nothing. Whenever client code expects a real object, it can also take a null object.

  • Also makes the client code simple. Clients can treat real collaborators and null collaborators uniformly. Clients normally don’t know whether they’re dealing with a real or a null collaborator. This simplifies client code, because it avoids having to write testing code which handles the null collaborator specially.

Disadvantages:

  • Can be difficult to implement if various clients do not agree on how the null object should do nothing as when your AbstractObject interface is not well defined.

  • Can necessitate creating a new NullObject class for every new AbstractObject class.

Question

Does a Null Object transform to become a Real Object?

No
Yes
Depends on compiler
Depends on C++ version
A Null Object does not transform to become a Real Object. There may be a real object with a do nothing mode, such as a controller which can switch in and out of read-only mode.

Is Null Object class is implemented as a Singleton?

Yes
No
Depends on compiler
Depends on C++ version
Since a null object usually does not have any state, its state can't change, so multiple instances are identical. Rather than use multiple identical instances, the system can just use a single instance repeatedly