Thursday, 5 June 2008

Checked Exceptions as Disclaimers

Dear Junior

Almost all codebases I have seen have had issues with exceptions. Considering that they are kind of mandatory to cover in even the first introductory Java course, it is surprising how poorly understood they are.

My favourite way of thinking about exceptions is to see them as disclaimers to a contract.

For example, if I employ a plumber to do some work with the plumbing in the kitchen, we will agree on some kind of contract. The contract does not actually do the work, but it specifies the conditions when the work is performed. So, to start with we will agree what the work will result in, in this case fixing the bathroom floor sink.

But then the plumber mentions that in houses build around the time my house was built there sometimes are very strange plumbing. Instead of the pipes being made out of metal and with clockwise fittings, these have ceramic pipes and anti-clockwise fittings. There is really nothing wrong with the ceramic, anti-clockwise plumbing - it does it job and is perfectly normal, just very unusual.

The plumber points out that she has neither the competence, nor the tools to fix that kind of plumbing. Therefore she asks me if I can ensure that it is the standard type of plumbing. Frankly, I have no idea. Negotiation goes into stale mode.

Now I have a choice. First choice is to employ the plumber to come and investigate the type of plumbing, and thereafter be sure that I can employ her to actually fix the plumbing. Second choice is to take the risk, employing her and saying "if it is the strange type, I understand if you can't do the job", and then have a plan to handle the situation. This is fine with the plumber, but she insists on writing this into the contract as a disclaimer: "the employer is not obligated to investigate the type of pipes in advance; however, if they are of non-standard type, then the plumber is not obliged to do the job".

After judging what would suit me best, I settle for the second option: a contract where I do not need to check in advance, where there is a disclaimer, and where I have to handle the unusual situation if it occurs.

Let us now take this story and put it in the context of designing classes, distributing responsibilities and defining separation of concerns in an object oriented system. We then end up with the contract metaphor described by e g Bertrand Meyer in "Object Oriented Software Construction", often referred to as "Design by Contract".

The first contract just describing the job would probably end up as an interface defining the method which the client can call.

interface Plumber {
void fixFloorSink(Bathroom);
}

The result of calling the method is a state-change in the bathroom. In Meyers terminology, this is a post-condition and we should document it in the javadoc. Even better, we should write a unit test, but that is kind of beside the point here.

/**
* Fixes the floor sink.
* postcondition: bathroom.drainsWaterOnFloor() == true
*/
void fixFloorSink(Bathroom);

Let us add the disclaimer. Obviously, this will show up as an exception. In the spirit of Domain Driven Design, we try to name the exception in a way that makes sense in the domain.

void fixFloorSink(Bathroom) throws NonStandardPipeException;

What kind of exception should NonStandardPipeException be? Well, according to the contract, the caller/client/house-owner/I does not need to check the condition in advance, but have to handle the situation. This maps neatly onto try/catch, so we make NonStandardPipeException
into an ordinary, checked exception.

class NonStandardPipeException extends Exception {}

A small note here is that this situation is not what Meyer calls a pre-condition. A pre-condition is something that has to be true before the method is called, and which the caller is obliged to check. But that will have to be the subject for some other letter.

Now for the client side, the house-owner I, will be forced by the compiler to take responsibility for its side of the contract, handling the situation if the disclaimer is raised.

void takeCareOfNonDrainingBathroom() {
try{
findPlumber().fixFloorSink(house.upstairsBathroom);
} catch (NonStandardPipeException nspe) {
fixPipesYourself();
}
}

Note the ambitious (or desperate) attempt to handle the exceptional situation. The important thing is when the try-catch block has finished; the resulted situations are equivalent, regardless what path the execution took - the contract of the method 'takeCareOfNonDrainingBathroom' has been fulfilled. If we cannot do that we in our turn should consider revising our contract, adding a disclaimer and throwing an exception.

So, an exception is our way to realise the concept of a disclaimer in a contract.

Yours

Dan

  • Bertrand Meyer, Object-Oriented Software Construction