Null object design pattern is one of the behavioral patterns. In the null object design pattern, an object is defined, which has no value or zero value, hence, a null object. The uses of such object along with their behavior are described by the null object design pattern. The ‘Pattern Languages of Program Design’ book series were the first to publish this pattern.
Null Object Pattern
According to the Gang of Four:
The intent of a Null Object is to encapsulate the absence of an object by providing a substitutable alternative that offers suitable default do nothing behavior. In short, a design where “nothing will come of nothing.
The pattern is implemented in object-oriented programming languages, for example, Java or C#. The implementation in these languages is such that the references may be null. Before implementing any methods, these references need to be checked in order to ensure that they are not null because the methods cannot be implemented on null references.
To forward the message of the absence of a particular object, for example, a client who does not exist, programmers do not use null references, instead of that, they use an object that serves the expected interface but its method body is empty. This approach has an advantage over the working default implementation, which is the predictability of the null objects. Also, it is not capable of doing anything and therefore, it has no side effects.
Sample Implementation of Null Object Pattern
For example, consider a function that retrieves a list of the files, present in a folder and performs an operation on each file. In case, it comes across a folder that is empty, it has to return a response which could be merely a null reference instead of a list. The design can get complicated when the code expects a list and it has to verify that it, in fact, has a list.
If a null object is returned, there is no need for the verification of the return value, whether it is a list or not. The object can be iterated as normal, without having to do anything, by the calling function. But, the return value still can be checked, that whether it is a null object or not. The null object design pattern can also be used a tool for testing.
Spring 5 Design Pattern Book
For example, to test the availability of a certain feature such as a database. The structure of the null object design pattern is such that it contains, Abstract Class, Real Class, Null Class, and Client. The abstract class has to define the abstract primitive operations which are defined by the concrete implementations. The Real Class is responsible for implementing the Abstract class, performing some real operations. The Null class implements the abstract class, which do nothing. It does that so that a non-null object is passed to the client. The client receives the implementation of the abstract class and uses it.
The fact that the received object is a null object or a real object, does not matter to the client because both of them are used the same way. The null object design pattern removes the old functionality and replaces it with null objects. The null object has a ‘do nothing’ code put in them, this is to avoid the blocks for do-nothing code. The advantage of doing it is that the present code does not need to be changed or altered.
UML Diagram of the Null Object Design Pattern
Let’s see the following UML diagram about this design pattern and it’s components classes.
Client
It requires a collaborator.
AbstractObject
It declares the interface for Client’s collaborator and its implements default behavior for the interface common to all classes, as appropriate.
RealObject
It defines a concrete subclass of AbstractObject whose instances provide useful behavior that Client expects.
NullObject
It provides an interface identical to AbstractObject’s so that a null object can be substituted for a real object.
Example for Null Object Design Pattern
Let’s see the following example with coding implementation step bu step.
Step 1: Create an interface.
Animal.java
/** * */ package com.doj.patterns.behavior.nullobject; /** * @author Dinesh.Rajput * */ public interface Animal { void makeSound() ; boolean doNilSound(); }
Step 2: Create concrete classes implementing the above interface.
Dog.java
package com.doj.patterns.behavior.nullobject; /** * @author Dinesh.Rajput * */ public class Dog implements Animal { String name; public Dog(String name) { this.name = name; } @Override public void makeSound() { System.out.println("woof!"); } @Override public boolean doNilSound() { return false; } }
NullAnimal.java
/** * */ package com.doj.patterns.behavior.nullobject; /** * @author Dinesh.Rajput * */ public class NullAnimal implements Animal { @Override public void makeSound() { System.out.println("No Sound!!!"); } @Override public boolean doNilSound() { return true; } }
Step 3: Create AnimalFactory Class.
AnimalFactory.java
/** * */ package com.doj.patterns.behavior.nullobject; /** * @author Dinesh.Rajput * */ public class AnimalFactory { public static final String[] names = {"Tommy", "Lucy"}; public static Animal getAnimal(String name){ for (int i = 0; i < names.length; i++) { if (names[i].equalsIgnoreCase(name)){ return new Dog(name); } } return new NullAnimal(); } }
Step 4: Use the AnimalFactory to get either Dog or NullAnimal objects based on the name of animal passed to it.
NullPatternDemo.java
/** * */ package com.doj.patterns.behavior.nullobject; /** * @author Dinesh.Rajput * */ public class NullPatternDemo { /** * @param args */ public static void main(String[] args) { Animal dog = AnimalFactory.getAnimal("Tommy"); Animal cat = AnimalFactory.getAnimal("NULL"); System.out.println("Animal's Sound"); dog.makeSound(); cat.makeSound(); } }
Step 5: Let’s run the above demo class and verify the output.
Animal's Sound woof! No Sound!!!