Showing posts with label design by contract. Show all posts
Showing posts with label design by contract. Show all posts

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


Wednesday, 5 December 2007

Don't Test Your Code

Dear Junior

From time to time I get asked how to test a specific piece of code (EJB2:s or Action classes being nasty examples). Most of the times I spontaneously do not want to answer the question at all, because I think it lead us in the wrong direction.

I do not want to test my code; I want to test the behaviour of my code. The distinction might seem like hair-splitting, but I think it makes an important point. For example, if I have some order payment service I want to check that the payment information is well-balanced, that a payment request is sent to the bank, and a lot of other stuff. If we "test the code" we want to ensure all of these things as they are written.

However, if we focus on testing the behaviour we want to make sure that one idea ("generated book-keeping transaction is well-balanced") is correctly realised by the code. This approach enables us to focus on one aspect of the behaviour at a time and ask: How can I make it possible to test this behaviour. And the natural answer is to restructure to obtain testability.

So, phrasing the test-question "how do I test my code" leads us to build really elaborate testing frameworks to feed test data into the database, and then running our components in a production-like environment (e g Cactus tests). On the other hand, phrasing the question "how do I test the behaviour of my code" leads us to refactor until we have separated concerns to such a degree that each idea is possible to test in isolation.

So, don't test your code; test its behaviour.

Yours

Dan

ps One thing you can do if you redesign for testability, is that you can refactor your code to require less cumbersome and awkward mocking.