Sunday, January 22, 2012

Writing good static argument validation methods

Before we get into the NullObjectProxy class there is one more prerequisite to get out of the way.
How many times did you have to write the following kind of code:
// Example (1)
if (parameter > limit)
    throw new IllegalArgumentException (
            "'parameter' is too large: " + parameter + " > " + limit);
I decided that I had enough of this and that I wanted to be able to write something like:
// Example (2)
validate (parameter > limit, "'parameter' is too large: " + parameter + ">" + limit);
However, a little thinking made me realize that this form is not really appropriate and would have annoying side effects. In this specific case a bunch of Strings would be allocated and concatenated each time the method is called and that is not good since it would make calling the method very different from the initial code. I did a little more thinking and I came up with:
// Example 3
validate (parameter > limit, "'parameter' is too large: &1 > &2", parameter, limit);
The implementation for this is shown below:
Here is the getFormattedMessage(..) method:
Now with this version what you have on each call is that a few arguments are pushed on the stack. You still have annoying side effects though. You still get an Object[] allocated and filled on each call. Also, if you have primitive arguments you get autoboxing of those into primitive wrapper classes (Integer, FLoat, ...). Of course you can further optimize this by having different versions of the method with different primitive arguments. Fortunately it is easy to define such methods for the most common cases. Here is an example with two int arguments:
public static final void validate (boolean condition, String formatString, 
              int firstValue, int secondValue)
Those methods are defined in a static utility class called Verification and this class contains one and two arguments versions for all primitive number types. Now, the validationFailure(String, Object ...) is called only if the condition fails and so objects are allocated only on failures. We at last have something close enough to the original construct that we can use on a routine basis.
I was so pleased with this that I added a validateNotNull(boolean, String, Object ...) method:
// Example (4a)
public static <T> T validateNotNull (T value, String formatString, Object ... arguments)
This is a generic method that will handle any type of reference and return the correct type (no cast needed). I also have a short form for this:
// Example (4b)
public static <T> T validateNotNull (T value, String parameterName)
This short form will print a predefined message:
parameterName should not be null
You can use those methods like this (short form):
// Example (5)
SomeType localVariable = validateNotNull (parameterOfSomeType, "parameterOfSomeType");
The verification class also contains a number of expect(boolean, String), expect(boolean, String, Object ...) and expectNotNull(Object, String) methods. Those are used to test other conditions that would normally throw IllegalStateExceptions.
Ok, now your thinking "that's a lot of trouble for an assert mechanism". The reason I don't use the Java assert mechanism for this is that:
1) I don't want it to be possible to disable those verifications and I want them enabled by default.
2) My Verification class methods have a very usefull trace mechanism build-in. When activated this trace will always get printed even if exceptions are thrown away or lost (handled improperly in thread, ...).
I really like those little methods and I use them a lot. They make it really easy to build meaningful error information reporting into the code.
Of course I will use them in my next blog about my new NullObjectProxy class.

No comments:

Post a Comment