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:
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:
And here the Null Object can help you:
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.
you should always use this pattern:
public List<String> returnCollection() {
//remainder omitted
if (/*some condition*/) {
return null;
} else {
// return collection
}
}
This basically prevents caller of your code to get NPE while trying to do things like this:
public List<String> returnCollection() {
//remainder omitted
if (/*some condition*/) {
return Collections.emptyList();
} else {
// return collection
}
}
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:
and the code that returns the reference to the User object looks like this:
public class User {
private String username;
private boolean authenticated;
// remainder omitted
public boolean isAuthenticated() {
return authenticated;
}
// remainder omitted
}
This way the code that checks whether our user is authenticated should look like the following snippet:
public User getUser() {
if (/*some condition*/) {
return user;
} else {
return null;
}
}
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.
if (obj.getUser() != null && obj.getUser().isAuthenticated() {
// allow
}
// remainder omitted
And here the Null Object can help you:
plus:
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 cleaner client code:
public User getUser() {
if (/*some condition*/) {
return user;
} else {
return NullUser.getInstance();
}
}
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:
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.
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).
I agree with Igor, this tactic is problematic. Check out Maybe Monad simulation in Java that wraps return value in an Iterable.
You don't really need a subclass. You class can have a static null object in itself.
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.
@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.
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.
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
Post a Comment