I have a instance of a class that might be missing, in which case the reference is null.
Use the NullObject pattern in place of null checks.
The null object pattern seeks to use an alternative, default or no-operation instance in place of a null
reference. Instead of having an object reference point to null it points to the "default" or "missing" instance.
The default instance needs to be able to be able to stand-in for the real instance within the code base. Code using the default instance should be able to call methods and interact with a default instance and produce the correct results (the Liskov substation principle applies).
Example Using a class
In the JobRole class example above we need to create a "null object" stand-in. Here I create an UnknownJobRole class that extends JobRole. The class that instantiates the Employee object graph then needs to know to use the UnknownJobRole class when an employee does not have a JobRole. This class will override any method in JobRole so it can be used in its place.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class UnknownJobRole extends JobRole { | |
@Override | |
public boolean isUnderSalaryBand() { | |
return false; | |
} | |
@Override | |
public boolean isHighTurnOverRole() { | |
return false; | |
} | |
} |
Now that I don't need to worry about the jobRole instance being null I can revise my method.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Employee { | |
JobRole jobRole; | |
public boolean isRaiseDue() { | |
return jobRole.isUnderSalaryBand() || | |
jobRole.isHighTurnOverRole(); | |
} | |
} |
Example Using Enum
When defining Enums, if the value could be missing be sure to add a default or unknown value. These null object enums are also handy if you need to sort a collection instances by the enum value. Remember enums will sort based on their ordinal (the order they are defined within the enum class).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public enum VendorType { | |
MANUFACTURER, | |
DISTRIBUTOR, | |
UNKNOWN; | |
} |
Using a collection
A simple way to avoid null checks for collections is to always use an empty collection. A empty collection can be viewed as a type of null object pattern implementation. The Collections class in Java 8 has been expanded to include several convenient methods to help with empty collections.
Summary
The null object pattern is the place to start when dealing with null values, especially in your domain classes. It is an elegant solution to the problem. However, it doesn't work for every situation and can introduce needless complexity. Pick the right solution for the problem at hand and don't let anyone tell you the Null Object Pattern is the only correct way to deal with nulls.