2

I am currently writing a small argument checking library for Java. Checks are written in a fluent interface way like this:

Check.that(name).matches("hello .*!").hasLenghtBetween(0, 20);
Check.that(list).isNullOr().hasSize(0);
Check.that(args).named("arguments").isNotEmpty();

So far the semantics of these checks is that they also implicitly assert that the argument is not null. To allow null, one can use the isNullOr() modifier method like in the second example.

The next thing I want to add is support for check inversion like this:

Check.that(name).not().matches("hello .*!");

But now I feel that the default nullness handling becomes weird and unintuitive. The correct way of inverting the test would be to allow null now. To disallow null, one would have to prepend the isNotNull() check explicitly:

Check.that(name).isNotNull().not().matches("hello .*!");

Because of this, I am thinking about changing the semantics so that nullness always needs to be checked explicitly. I know of one project that also does this: Bean Validation. But the downside is that this will probably make about 90% of the checks 12 characters longer, as null is often an invalid argument anyway.

So, to make a long story short: What are the arguments for and against implicit null checking? Maybe there are any other libraries or standards that do it this or the other way?

rolve
  • 10,083
  • 4
  • 55
  • 75
  • Did you get a chance to look at http://commons.apache.org/lang/api-2.6/org/apache/commons/lang/Validate.html? – Vikdor Sep 23 '12 at 13:17
  • Yes, they went with the implicit strategy. But that fact alone doesn't help me *much*. – rolve Sep 23 '12 at 13:24

2 Answers2

2

I would keep the API fairly simple and not sensitive to method calling order - basically a fluent builder pattern. To do this, make the following changes:

  • make it stateful (use an instance rather than static methods)
  • make the actual check the last method call

Each method call would build up the criteria to be executed, then finally you execute the check.

Like this:

new Check().matches("hello .*!").hasLengthBetween(0, 20).check(name);

This would also allow reuse of Check instances - something your stateless (static) version can not do.

Further, if the order of method calls does not matter, it avoids totally the use of "logical operator" methods, which will only lead to grief, and where do you stop?. Consider this ridiculous scenario:

new Check().matches("hello .*!").hasLengthBetween(0, 20)
    .and().openBracket().isLowerCase().or().containsNumbers().closeBracket();
Bohemian
  • 412,405
  • 93
  • 575
  • 722
  • Thank you for the answer. However, I don't quite see the advantages of your approach. First, I am already using instances (`that(...)` returns one) and second, I am also already reusing those. Each overloaded `that(...)` method always returns the same object (in a given thread). – rolve Sep 23 '12 at 14:09
  • Yes but you *start* with the thing to be checked, so method call order matters, because checks have to be run at every call. If you check last, you can use logic such as automatically null checking if any of certain other methods have been called. Even if you leave your API as it is, at least don't offer logical operators. – Bohemian Sep 23 '12 at 14:26
  • I considered doing all the checks at the end. But this approach has a serious problem: If someone forgets the last method, no checks will happen. And this is a bug that is very hard to spot, since the line will look pretty reasonable (except of course that the argument is not used). But thank you for the advice. I will definitely not include any binary operator like `or()`. But the `not()` alone, with a scope of just the next method, doesn't seem to be that bad of an idea (let me know if you don't agree)... – rolve Sep 23 '12 at 14:41
1

I think I found a sweet compromise: All checks do an implicit null check but not() only inverts the actual check and not the null check. This way, I can write

Check.that(message).not().containsAny(badWords);

and assume that message is both non-null and contains none of the bad words. I think this captures more than 90% of the use cases.

And of course, I can still allow null explicitly by writing

Check.that(message).isNullOr().not().containsAny(badWords);

The second advantage is that this design separates the nullness concern from the rest of the checks, which is probably more intuitive too.

rolve
  • 10,083
  • 4
  • 55
  • 75