Friday, December 12, 2008

Null Object - design pattern

Joshua Bloch in his excellent book Effective Java (2nd Edition) gives advice that you should never return null collection/map/array from your code i.e. instead of such code:

public List<String> returnCollection() {
//remainder omitted
if (/*some condition*/) {
return null;
} else {
// return collection
}
}
you should always use this pattern:

public List<String> returnCollection() {
//remainder omitted
if (/*some condition*/) {
return Collections.emptyList();
} else {
// return collection
}
}
This basically prevents caller of your code to get NPE while trying to do things like this:

if (obj.returnCollection().size() > 0) {
// remainder omitted

Robert C. Martin in his book Agile Software Development, Principles, Patterns, and Practices gives another very similar pattern but related to ALL objects, not only to collections/maps/arrays. This design pattern is called Null Object.

Here is the example - let's assume that you have an application that check whether the user is authenticated:

public class User {
private String username;
private boolean authenticated;
// remainder omitted

public boolean isAuthenticated() {
return authenticated;
}
// remainder omitted
}
and the code that returns the reference to the User object looks like this:

public User getUser() {
if (/*some condition*/) {
return user;
} else {
return null;
}
}
This way the code that checks whether our user is authenticated should look like the following snippet:

if (obj.getUser() != null && obj.getUser().isAuthenticated() {
// allow
}
// remainder omitted
Checking whether the object is null is not only a boilerplate code but it can also give you a lot of bugs e.g. if you forget to check whether the object is null.

And here the Null Object can help you:

public class NullUser extends User {

public static final NullUser INSTANCE = new NullUser();

public static NullUser getInstance() {
return INSTANCE;
}

@Override
public boolean isAuthenticated() {
return false;
}

private NullUser() {
}
}
plus:

public User getUser() {
if (/*some condition*/) {
return user;
} else {
return NullUser.getInstance();
}
}
plus cleaner client code:

if (obj.getUser().isAuthenticated() {
// allow
}
// remainder omitted

I find this pattern very useful and really helpful. With this pattern you can really save yourself a lot of NPEs.

Still the question is whether User should be a class or an interface and then whether NullUser should extend the base class or implement the user's interface. But I will leave this decision for your consideration.

What do you think about Null Object pattern?

PS. Example I presented is not necessarily applicable in the real systems - it is here just to depict design pattern's idea. Please don't treat provided code as a solution (I can think of many improvements/changes to it depending on the context by myself) - think about it at the pattern level - not the code level.

8 comments:

Akash Kava said...

Yes, infact I always avoid null objects in order to reduce NullPointerExceptions, infact each subsequent code could require extra not null check, and in case of complex compound comparisons, checking not null each time can be quite complex.

Igor K. said...

I don't like the example.
What you call NullUser should be called NonAuthenticatedUser. This is the only known characteristic; it inherits everything else from a default User.

The pattern is bad by a number of reasons. What if you get a user, but instead of checking whether it's authenticated, you want to check if its name is valid? You would need to override all the possible queries with "return false" in your NullUser implementation. Which is error-prone. Every time you add a new function to the User, you need to think about NullUser behavior. Or, if you want the compiler to help, make User an interface, call another class RealUser and this one NullUser. To me, this is ugly: now you would be forced to implement every function of RealUser in the NullUser.

Another reason the example is bad: what if you want to find the reason the user is not authenticated? You can't distinguish between non-authenticated user and null anymore. (Of course, you can check the class of the object, but it's no better than checking for null).

Yardena said...

I agree with Igor, this tactic is problematic. Check out Maybe Monad simulation in Java that wraps return value in an Iterable.

Anonymous said...

You don't really need a subclass. You class can have a static null object in itself.

pope said...

Thanks.

I do agree with some of the comments: that maybe this isn't the best example, but the lesson is on point for me.

Another thing to consider though is what would happen if you had a UserService interface? Then you would call something like myUserService.isAuthenticated(obj.getUser()). Inside of that UserService implementation, you can handle the nulls in one place without having to extend the User class. This also separates data from logic leaving your User class as just a POJO.

Przemysław Bielicki said...

@pope

You're right - you can do the check in many places - there is no one best solution. However what I meant to say is that it's much better for developers to avoid checking objects for null (not everywhere and not for all price).

You can even imagine the implementations of Null Objects that will throw UnsupportedOperationException for all of some of the invoked methods (like many methods from collection views from Java Collection Framework).

OK you replace one exception with another but UOE is better IMO than NPE.

Anonymous said...

It's assumed you know you're getting an object back. Therefore, it's assumed you check for null as part of the standard coding practices. Programming is full of assumptions. Making sure they are verifiable is the goal of Computer Science. You can't assume that a programmer is oblivious to basic constructs while working on code that is more complex. What's more, this creates a difference in interface between collections which can be empty and those that should not be.

Lasu aka Marek Kozieł said...

I suggest u look at:

http://www.jroller.com/scolebourne/

http://docs.google.com/View?docid=dfn5297z_3c73gwb

http://lasu2string.blogspot.com/2008/12/null-handling-small-language-changes-on.html