Wednesday, May 5, 2010

Programming by contract

While I haven't mentioned it, I've touched upon one of the most important ideas for improving software quality. As you may have noticed in the function prototypes, I did not only add an exception specifier, there was also a comment mentioning a precondition. That precondition is a contract between the caller of the function and the function itself. For example, the precondition for "divide" is "dividend != 0". This is a contract saying, "I'll do my work if, and only if, you promise not to give me a zero dividend." This is important, because it clarifies who is responsible for what. In this case, it means that it's the callers job to ensure that the dividend isn't 0. If the dividend is 0, the "divide" function is free to do whatever it wants. Throwing an exception, however, is a good thing to do, because it gives the caller a chance. Another very important part of programming by contract, that I have not mentioned, even briefly, is something called a post condition. While the precondition states what must be true when entering a function, the post condition states what must be true when leaving the function (unless left by throwing an exception). The functions used above do not use post conditions, which is very bad. Post conditions check if the function has done it's job properly, and if used right, also serves as a documentation for what the function does. Take for example the "divide" function. A post condition should say something about the result of it, in a way that explains what the function does. It could, for example, say:
Postconditions:
dividend*result<=divisor
dividend*(result+1)>=divisor
This states two things: It *is* a division function, not something else just happening to have that name, and the result is rounded and will stay within a promised interval.
To begin with, scrutinously using pre- and post-conditions force you to think about how your functions should behave, and that alone makes errors less likely to slip past. They make your testing much easier, since you have stated clearly what every function is supposed to do. (If you haven't stated what a function is supposed to do, how can you be sure it's doing the right thing?) Enforcing the pre- and post-conditions makes errors that do slip by anyway, easier to find.
When using "Programming by Contract", exceptions are a safety measure, pretty much like the safety belt in a car. The contract is similar to the traffic regulations. If everybody always follows the rules, and when in doubt, use common sense and behave carefully, no traffic accidents will ever happen. As we know, however, people do break the rules, both knowingly, and by mistake, and they don't always use common sense either. When accidents do happen, the safety belt can save your life, just as exceptions can. Note that this means that exceptions are *not* a control flow mechanism to be actively used by your program, just as much as the safety belt isn't (someone who makes active use of the safety belt as a control mechanism of the car would by most people be considered pretty dangerous, don't you think?) They're there to save you when things go seriously wrong.
OK, so, in the light of the above, have you found the flaw in my test program above yet? There's something in it that poorly matches what I've just mentioned above. Have a look again, you have the time I'm sure.
Found it? Where in the program do I check that I send the functions valid parameters? I don't. The whole program trust the exception handling to do the job. Bad idea. Don't do that, not for pre- and post-conditions anyway. Pre- and post-conditions are *never* to be violated, ever. It's inexcusable to violate them. That's actually what they're for, right? When having a precondition, always make sure you're conforming to it, don't trust the error handling mechanism. The error handling mechanism is there to protect you when you make mistakes, but you must always try to do your best yourself.

No comments:

Post a Comment