Lesson from Object Bootcamp – The Null-Object Pattern
I thought I would share a few of the things that I learned while going through Object Bootcamp last week. The first, and probably the biggest thing was the idea of memory equivalence. This can be show easily by also showing another thing that I learned which was how to use the Null-Object design pattern. This is a pattern that I have read about, but have never really seen used until now.
The Null-Object pattern is used to return a null object instead of a null. The former is better since it removes the chance of encountering a NullPointerException. The idea is to return an object that basically does nothing, i.e., its methods simply return default, useless data. The trick is in how to implement this object.
Say we have an email object that represents an email address and can send an email when the appropriate method is called; however, the email address may or may not exist. So instead of returning null, we want to return a null-object when we do a search. So here is our email object:
public class email {
private String emailAddress;
public boolean sendEmail(String message) {
// send out email using Javamail or something
}
}
Now here is the same class utilizing the null-object pattern, the trick is the static instantiation.
public class Email {
public static Email noEmailAddress() {
public boolean sendEmail(String message) {
return false;
}
};
private String emailAddress;
public boolean sendEmail(String message) {
// send out email using Javamail or something
}
}
The sensible default is to always answer false (i.e., the email was not sent) when sending the sendEmail() message to the Email object. This way, it does what we expect and doesn’t break anything.
*Update:* ignore the following since, although it does make the code more readable, it breaks with the intention of the null-object pattern. As pointed out to me in the first comment.
Another cool feature of using the null-object pattern is that we can now write code that looks like this:
if (returnedEmailAddress==noEmailAddress) {
// do something about it
}
Instead of checking to see if the result is null.
if (returnedEmailAddress==null) { //do something }
This makes the code a lot more readable and understandable. Just by looking at the code, we can understand immediately what happened. The trick to this is that we only ever instantiate one noEmailAddress object. This allows us to check that the two references point to the same space in memory and results in a very nice equivalency check instead of having to use returnedEmailAddress.equals(noEmailAddress).
The whole point of the Null Object pattern is that you *never* check if the object is null, you just call a method and it does the right thing — nothing if the object is “null” and something if the object is “real”.
E.g. if you have statements like “if (returnedEmailAddress==noEmailAddress) {… ” in your code you are not following the Null Object pattern (or OO design principles) fully. The branches of the if statement should be moved into the appropriate implementors of the Email interface and the explicit conditional replaced by a polymorphic call.
Good point. And I get to learn something else. I have updated my post and removed the last few bits to reflect your comment. I agree, the point of the pattern is to ensure that you do not need to check what was returned. You can simply assume that the returned object is good and rely on the defaults to prevent any problems.
I would replace the ‘never’ with ‘never, except at your module boundary’. Returning a NullObject to someone outside of your module is at least as bad as returning a null. NullObjects are generally quite specialized to exactly the operations you need them to quietly ignore. As soon as someone needs to check for a NullObject or it makes your code complex, you’ve lost.
Oh, and on your example: Java isn’t quite that concise, and having a method return it rather than a constant won’t ensure you have only one copy. I think this is what you meant:
public class Email {
public static final Email NONE = new Email() {
public boolean sendEmail(String message) {
return false;
}
};
private String emailAddress;
public boolean sendEmail(String message) {
// send out email using Javamail or something
}
}
Hmmm.
Nate’s example is nice for testing (stub-a-licious). Probably not nice for production code.
Fail fast and NPE if there’s not a useful implementation. Defaulting to a null object seems rather dangerous and makes it harder to find subtle bugs.
Use dependency injection of a “simple” or “does nothing” or “null” implementation is really really what you’re after. It should respond with “situation normal” just like a real implemenation would.
In your scenario, if there was no mail sending aparatus available, the “null” emailer probably should behave like everything was fine. Returning false would indicate something should have happened but didn’t and the code needs to deal with it (Exceptions, anyone?). If you’ve explicitly wired in a “do nothing but be happy about it” implementation then it should return true and the code continues as everything has eventuated as desired.
Perhaps.