Empty Collections instead of null.
The easiest way to prevent null pointer exceptions when using collections is to use empty ones. The Java collections library in fact includes empty versions of all the collection interfaces. Use them. It takes a little more typing, but it prevents one more check. And usually, most collection code can be written to be size agnostic.
Use meaningful defaults.
This one is really simple. Instead of being lazy, think about good defaults for your objects or at least ones that will be easy to spot when debugging. One of the problems that I have with nulls is that when I have to debug code, they tell me nothing about the intention. A default can give some sort of clue. If defaults can not be used, then...
Throw an exception.
But, if you must insist on using nulls, then please don't use them for incorrect usages of your API. In other words, if I pass you bad parameter, don't return null because your API can't handle it. Signal an exception and tell me what the problem is. Sadly, the Java collection API falls into this trap. The implementation of Map returns a null if it doesn't find the key and null is ambiguous in this case because it can be the key was not found or that null was stored as the value. I think an exception for the first case would prevent that. It would also make it easier to spot an incorrect usage of the API with a good message to tell me what to correct. Good designs are easy to debug and require little to no detective work. Be nice to your users.
Use small objects.
Wrap primitives into something more meaningful. You can then use the interface for both the implementation of real values and another one for true Undefined scenarios. Small objects are easy to debug because they can give much more meaningful information and you don't have to deal with all knowing objects that are 300+ lines of code. Small objects make dealing with nulls easier because...
Null object Pattern.
This pattern described in "Pattern Languages 3" by Bobby Wolfe. Go read it now if you haven't. Basically, implement a different implementation of your interface that either deals with the undefined case more gracefully. Now, by that, I mean, either throw an exception detailing why the request could not be completed, ignore the request, or forward it on to someone else. There could even be more options, but the first two are the most commonly used which of course, brings me to...
Deaf object pattern.
This is the Null object pattern where the implementation of the interface ignores all exceptions. This is used throughout Dolphin Smalltalk to great effect. This is a dangerous pattern, but when used right can make life easier. Dolphin uses it as a placeholder for objects that are not fully instantiated yet and requests would not make sense. It does this external resources where the object is instantiated, but they have not yet allocated the proper resources yet to make it fully functional. It helps them avoid timing issues. Again, I would caution the overzealous use of this pattern because it can mask bugs which brings me to...
Undefined object pattern.
Yet another popular variant of the Null object pattern, this version throws an exception with a meaningful message (be creative, but put something better than "undefined object<>
In closing, the one point I want to make is relying less on null requires thought in your design. Too many times, I see use of nulls as absence of caring for the users of your code. Nulls are just lazy and you can replace most of the them in your code using the above arsenal. Plus, the techniques above can make your code more readable, easier to debug, and more fun to extend.
No comments:
Post a Comment