1.6 Runtime Checking
So far, we have concentrated on the role that contracts play in specifying the behavior of a component. In this role, contracts are intended to be read by people. But contracts can also be checked at runtime. In this section, we'll see one example of what happens if you write careful contracts and then accidentally introduce bugs into the code. You might not be convinced by the example because it is so simple. More powerful examples of the benefits appear in later chapters.
Suppose, for example, that we got confused about the precise meaning of the add feature and believed that we could use it to change the details associated with an existing customer. Suppose we called add with a customer details argument whose id was already active. In a programming environment that understands contracts, we would be told something like the following (we'll assume the CUSTOMER_MANAGER component has been implemented by a class of the same name):
Stopped in object [0xE96978] Class: CUSTOMER_MANAGER Feature: add Problem: Precondition violated Tag: id_not_already_active Arguments: a_customer: BASIC_CUSTOMER_DETAILS [0xE9697C] Call stack: CUSTOMER_MANAGER add was called by CUSTOMER_MANAGER_UIF change_customer
This is the level of detail provided by the Eiffel development environment supplied by Interactive Software Engineering, Inc. Other environments provide similar detail (including other Eiffel environments and environments that add design by contract facilities to other programming languages, such as Java and C++). Working through this wealth of debugging information line by line, we can tell
That the application has stopped in some object (we could open the object with an object browser and examine its attributes).
That this object is of the class CUSTOMER_MANAGER.
That a problem arose when that class's add feature was called.
That the problem was that some part of the precondition on add was violated.
That if a precondition is violated, it means some client called the add feature when it was not legal to do so. Specifically, it was the part of the precondition with the id_not_already_active tag that was violated.
Which BASIC_CUSTOMER_DETAILS object was passed as an argument to the call.
The sequence of calls that led up to the problem: A change_customer feature in a CUSTOMER_MANAGER_UIF class (the user interface to the customer manager application) called the add feature in the CUSTOMER_MANAGER class.
Putting all this information together leads us to conclude that the change_ customer feature in CUSTOMER_MANAGER_UIF class is the cause of the problemit called add with a BASIC_CUSTOMER_DETAILS object whose id was already active, and the contract says that's illegal. In other words, the change_customer feature contains a bug, which we must find and fix.
Developers who make good use of contracts come to expect this amount of help when something goes wrong. They don't spend long hours hunting for the causes of a runtime error. They put their effort into writing contracts instead. And, in return, they get a second benefit, trustworthy documentation, which is the subject of the next section.
Just before we leave this section, though, we should explain that you might want contracts checked while you are developing an application, but you might not want production code to be slowed down by all this checking. In a programming environment that supports design by contract, you can turn contract-checking on and off. You can turn it on in some classes and off in others. And you can turn it on at different levels, such as checking only preconditions, rather than full checking of preconditions, postconditions, and invariants.