The Beauty of the Null Object Pattern
Before we start...
Let me start by saying this: NEVER dismiss an idea without giving it any consideration. That was the mistake I made when I first encountered an article about this design pattern many years ago. I was thrown off by the name and without even attempting to find out what this design pattern was all about, I moved on to other things. That was a huge mistake and I am not afraid to admit it. I urge you not to make that mistake. And if you have already, never make that mistake again. Trust me, you will thank me later. For now, let me tell you my story and attempt to explain when to use it along with a simple example on how to implement this pattern.The Null Object Pattern
By definition, the main purpose of this pattern is to have an Object-Oriented alternative to the absence of objects by creating objects that display "do nothing" behavior. That sounds like a mouthful. In simplest terms, it is meant to provide a mechanism to replace null references with actual objects instances that represent "nothingness." If you explore the origins of the number zero, you should be able to understand why having an object to represent "nothing" makes sense. The motivation behind this pattern is to allow applications to continue to work normally even if a null reference is encountered. It also makes your code much simpler because null objects should eliminate the need for conditionals to check whether an object is null prior to using it. For the most part, the diagram below illustrates the typical Null Object pattern. In the simplest of terms, a base class (or preferably an interface) with many sub-classes where one of these is the Null Object class. However, where many sources of this topic fall short, in my opinion, is to point out a very crucial characteristic the Null Object class must have: it MUST be an immutable class. It would be extremely bad if your null object, out of the sudden, mutates and it is not null anymore.Typical UML diagram of Null Object Pattern
So, my three recommendations on how to design your null object class:
- Make your Null Object class final
- Don't have any fields in your Null Object class that are null themselves
- Override all getters and setters to "do nothing"
Make your Null Object class final
The point behind making the Null Object class final is to prevent the existence of a sub type of the Null Object class to be not null. In other words, you cannot have a sub class to represent some thing when its super type represents "nothing." That goes against all logic. In Java, this is accomplished by using the final keyword in the class declaration:
public final class NullObject {
// body of class omitted
}
Don't have any fields in your Null Object class that are null themselves
public class SuperClass {
private int x = 3;
private String text = "Hello there!";
public SuperClass() {...}
public SuperClass(int x, String text) {...}
// setters and getters omitted
}
public final class NullObject extends SuperClass {
// setters and getters class omitted
}
public class SuperClass {
protected String text;
// setters and getters class omitted
}
public final class NullObject extends SuperClass {
public NullObject() {
text = "null value"; // field in superclass is no longer null;
}
// setters and getters class omitted
}
NullObject nullObj = new NullObject();
System.out.println(nullObject.text);
System.out.println(nullObject.getText());
Override all getters and setters to do nothing
public class SuperClass {
private int x = 3;
protected String text = "Hello there!";
public SuperClass() {...}
public SuperClass(int x, int y, String text) {...}
public int getX() {
return x;
}
public String getText() {
return text;
}
public void setX(int x) {
this.x = x;
}
public void setText(int text) {
this.text = text;
}
}
public final class NullObject extends SuperClass {
public NullObject() {
text = "null value";
}
@Override
public int getX() {
return 0; // or some other value that might make more sense in a "null object"
}
@Override
public String getText() {
return text;
}
@Override
public void setX(int x) {
// do nothing
}
@Override
public void setText(int text) {
// do nothing
}
}
My story ("real world" example)
About a year ago, while gathering information about Design Patterns to create training materials to train junior developer at my current job, I came across an article on Null Object pattern and that led me to Christopher Okhravi's video on the Null Object pattern. If you haven't seen any of his videos, I strongly recommended you watch them and subscribe. His delivery is simply amazing. Moving on, I was amaze that this "thing" I thought it was dumb and without merit, was in fact very powerful. So I decided to use it in my application. Without getting in the details of what my code does, just know that my application needs to consume different "models" in order to get certain configuration items that allow me to interact with different other applications. So, back then, I made the life-changing decision to give this pattern a try. My main motivation was that, if I was to query for a particular model name and the name did not exist, I should be able to continue operating without even having to check for null references.
currentModel = modelMap.entrySet().stream().filter(e -> key.equalsIgnoreCase(e.getKey()))
.map(Map.Entry::getValue).findFirst().orElse(new NullModel());
public final class NullModel extends Model {
@Override
public List getModelElements() {
List elements = new ArrayList<>();
elements.add(new NullElement());
return new UnmodifiableList<>(elements); // to guarantee immutability
}
@Override
public void setModelElements(List elements) {
// Do nothing
}
}
currentModel = switchModel("validKey");
System.out.println("Model name: " + currentModel.getName()); // Displays a real model name
currentModel = switchModel("invalidKey");
System.out.println("Model name: " + currentModel.getName()); // Displays "N/A" instead of null
Final words
The simplicity and power of this design pattern blew my mind once I decided to give it serious thought and consideration. And, it blew my mind even more when I saw pay dividends almost a year later. I hope you enjoyed reading this as much as I enjoyed writing it for you. I would like it very much for you to share your thoughts on the comments section. I would also love for you to subscribe to my blog and to my YouTube channel. I am planning to add more contents on a regular basis. Thank you, and I will see you around.Here's a link to my video example on YouTube https://youtu.be/cqVqOpdPMHA
Comments
Post a Comment