Quick and Dirty DBC
November 5th, 2007I really like the idea of Design-by-Contract (DBC), but when I tried to use it in my own code, I found a few problems. It wasn’t that DBC was particularly flawed, it was just that I usually program in Java and it isn’t part of the language as it is in Eiffel. This meant that I had to use some extra external tools to enforce the “contracts” I had written into my Javadoc. This turned out to be a bigger pain than I had anticipated. Things may be better now, but I discovered that either the tools I wanted to use were not actively being maintained or were just hard to use. They also added an extra “instrumentation” processing step to the normal edit/compile/run cycle that became something more to manage. I think, today, if I used AspectJ, I could probably make things run more transparently with less management on my part, but it wasn’t around with I came up with the following solution.
What I thought was the most useful and powerful feature of DBC was the “class invariant”. This was a check that the contents of a class instance were “correct”. Basically, it is a kind of “self test” on the state of the data structures in a running program. Given the lack of general knowledge and discussion of DBC, at least that I’ve observed, this little “gem” of an idea seems to have gone relatively unnoticed. As programmers, we generally don’t have a clue about the state of our running code until we notice something that isn’t quite right (a bug). Then we fire up the debugger and manually take the code out for a spin. With the class invariant we instrument our code such that we can ask the question “Is everything ok?” and have the code itself look for problems.
My solution is to dump the overhead of external tools and instead simply implement the DBC “class invariant” as a method within the class itself. This has the advantage of having no external dependencies and of making its invocation be completely under my control. To aid my efforts, I created a simple interface called SanityChecker that contains a single method sane() that returns true if the class instance is “sane” (i.e., validates the class invariant contract) or false if it is “insane” (i.e., a bug has been detected).
public interface SanityChecker {
boolean sane();
}
The trick to its usefulness is to implement it such that it throws an assertion exception if it detects that something is wrong. Here’s an example:
void sane() {
boolean retValue = super.sane();
// The length of the jobs queue should be less than ten
retValue = retValue && (jobs.length() < 10);
assert(retValue);
return retValue;
} // sane
Basically, this makes sure the super-class thinks it is “sane”, and then asserts its own sanity.
The idiom retValue = retValue && <boolean test> followed by an assertion of the value of retValue is a typical pattern. If followed, then once a problem is detected, an assertion will be thrown, or the rest of the tests will be skipped and the value of false returned. By having sane() return a value rather than simply relying upon the assert keyword sanity testing can occur even if assertions are not enabled.
The sane() method should be called when the instance is expected to be “stable” so the consistency checks should complete with no problems (if all is well). Typically, I find I use it like this:
main() {
// Do my big processing loop
while(true) {
assert topDataStructure.sane();
// Do the rest of the work here
} //while
} // main
The assert statement causes the state of the class instance referenced by topDataStructure to be validated before my code makes any changes to it. I implement the sane() method such that the class also asserts the sanity of any other classes it references (that implement the SanityChecker interface). They in turn, do the same. This causes the validation process to ripple throughout the program’s class instances (be careful to avoid cycles) and ensures that everything is as expected before the next iteration of the main processing loop gets its chance to muck things up. As a bonus, if a problem is detected, it is usually pretty easy to figure out which iteration of the loop is the culprit, this makes debugging much easier.
The net effect, for me at least, is that my code either runs, basically “bug free”, or it throws an exception and stops. And, if it does stop, the exception pinpoints the problem for me with high precision. Also, if I disable assertion checking (i.e., no “-ea” flag) then none of this checking is performed and the system runs without any baggage. The solution is simple, flexible, doesn’t require external tools and is extremely powerful; I’m happy.
Taking this idea further, one could imagine a simple extension to Java in which the class Object would include an additional method called sane(), like it does with toString(). As described above, it would return true by default and any classes that wanted to, could override it. It would be “simple” to trigger a debugger to run a “sanity check” and have the contents of the heap be validated automatically. This would be a lot simpler and more accurate than manually digging through with the debugger to “eyeball” class instances and make sure they look “ok”.