Monday, December 31, 2007

Today's Java Irritant: Design-by-Contract Disconnects.

Design-by-contract systems enforce API behavior. In Eiffel, contracts specify both internal and external behavior, at the language level.
connect_to_server (server: SOCKET)
require
server /= Void and then server.address /= Void
-- etc.
end

This does what it looks like, at runtime. (/= == != :)

The cost of doing the same in Java is higher; most frameworks use some form of aspect-oriented programming. This isn't a bad thing in itself, but gives some people (managerial types and unimaginative programmers) the heebie-jeebies. Dealing with issues created by lots of aspects can be daunting, but are relatively straight-forward in a well-educated Java house.

Lighter-weight solutions exist, may be preferable in many situations, and are easy to grok. Putting a chunk of precondition tests at the start of a method is trivial:
public String foo(final String bar_) {
Pre.notBlank(bar_);
// etc...
}
Here, the issue is that there's no automagic way to track or document preconditions other than either manually documenting them in Javadocs (preferably through a simple doclet tag, like @pre, or whatever) , or by programmatically scanning the source and pulling out "useful" information and doing something useful with it. Neither are particularly appealing: Javadocs go stale quickly, and writing a robust source parser is non-trivial.

Another answer is to use one of the annotation- and/or aspect-based solutions (or XDoclets, but... ew?) and swallow the bitter pill that is Java, and pay the cost of educating your developers. The packages I'm currently considering are Contract4J, which uses pre-built AspectJ aspects, and SpringContracts (which is nice since most my projects use Spring).

4 comments:

Ben Manes said...

One of the limitations of writing conditional checks in the method is that its not inherited. This is one of the nicest parts about DbC. An annotations/xdoclet approach gives most of that flexibility of defining the contract in the interface (where it belongs) but I think the inheritence semantics will be slightly different from Eiffel due to overriding contracts.

Dave Newton said...

That's an excellent point that I hadn't thought of; thanks!

prodrive555 said...

What about asserts as a basic level check? I see these used quite a bit in the Spring source.

Dave Newton said...

Sure, asserts are fine too. They suffer from the same drawbacks, though, in that they can't be (trivially) pulled out as documentation, and they may not provide the type of exception handling the app needs.