The way I'm checking those arguments is rather tedious. Usually I'm using if/else statements to make sure everything is as expected. It's a manual process of syncing code and documentation, which is cursed to fail eventually.
Various people are suggesting you should use assertions to enforce design by contract. Assertions? I know, I know! In case you have forgotten, like me, or never heard of in the first place, here's what assertions are in the Java space:
An assertion is a statement in the Java programming language that enables you to test your assumptions about your program....The assertion statement has two forms...:
assert Expression1 ;
assert Expression1 : Expression2 ;
There are many situations where it is good to use assertions...:
- Internal Invariants
- Control-Flow Invariants
- Preconditions, Postconditions, and Class Invariants
Frankly, I have never used a single assert statement in production code. It always felt kinda uncomfortable to give up control to a single statement and I don't like the the fact that you can switch them off. If someone runs your code without -enableassertions and is not aware of asserts being used, they might observe strange behavior. Another thing that seems weird is the different opinions on the purpose of assert statements.
The simple assertion facility does enable a limited form of design-by-contract style programming. The assert statement is appropriate for nonpublic precondition, postcondition and class invariant checking. Public precondition checking should still be performed by checks inside methods that result in particular, documented exceptions, such as IllegalArgumentException and IllegalStateException.
Usually you don't want a "kind-of-design-by-contract" facility, which is only supposed to be used in parts of your code. First of all, you want to enforce contracts at an API level, which assertions are not intended to be used for. Second of all, if internal contracts are needed, I think you should use the same facility as in every other place of your code, in order to have a consistent way of getting things done. In my opinion it doesn't make sense to change your programming style and use assertions only internally. I'm always cautious when it comes to the performance of my code. The JVM can eliminate unnecessary if/else statements, but it seems to be a little different with disabled asserts in short running applications. In addition, performance is significantly degrading when running your code with assertions enabled, but to be fair, it looks like it's similar to an if/else statement approach. So if assertions are kinda broken and if/else statements are tedious and error prone, what's left? Maybe Annotations? A recent article on InfoQ, gives an overview of JSR-305:
JSR-305, led by FindBugs' creator Bill Pugh, is looking to create both a standard set of annotations that analysis tools can use, and a mechanism to allow developers to add additional annotations as they need to. The current proposal includes annotations for nullness, sign, language, and threading amongst others.
I think this sounds quite promising, especially the well defined mechanism to create your own annotations in addition to the standardized set. However, my feelings about annotations are ambivalent. I like them because they are easy to use and you get some kind of static typing with all its benefits. They also do a great job in documenting your code. I think one downside of annotations, though, is overuse. Almost every framework provides a set of annotations, mostly in replacement for XML configuration. If every class and method is annotated multiple times, your code will become virtually unreadable. Another thing that bothers me are 3rd party annotations. They are another dependency on your classpath, which need to be maintained. I think I'll stay with my error prone and tedious if/else statement technique. It'll do just fine for checking arguments of API methods - at least for now. However, JSR-305 might convince me to give up some control and let annotations take over assertion of arguments.
3 comments:
Hey David,
that's kinda intresting. Considering the aspect of self documenting code I actually feel quite comfortable with at least checking all public method's parameters and throwing the appropriate exceptions and documenting them via JavaDoc. Besides handling this with if / throw, Spring provides an Assertion API in its util packages. To minimize dependencies to it some of my project have decided to wrap these Assertions with own (actually only delegating code) to maintain the choice of the actual Assertion implementation.
Regarding contract first style of API definition there is an intresting project that was discussed at spring user group a while ago:
http://sourceforge.net/projects/springcontracts
Regards,
Ollie
Hmm, from a design-by-contract point of view it's a sad state of affairs that you can turn off assertions in Java. If they were not and you could also decide which kind of exception to throw they'd be much more useful. That still doesn't document your constraints in the javadoc but at least it would be a start.
But there's something you can do with minimal hacking: Write a few utility methods for argument checking that use hamcrest matchers. Hamcrest can do a lot more than unit testing, and something like assertThat("arg", notNullValue()) or assertThat("arg", is(not(0))) looks concise and readable. Maybe define some additional matchers, too.
Cheers,
Matt
Thanks for the hints! SpringContracts look really nice, although this might go a little beyond argument checking...As for hamcrest, it's really cool and it might even a suitable replacement for if/else statements, although SpringContracts might be less intrusive in for that matter.
Post a Comment