3.2. Interface Contracts

 <  Day Day Up  >  

A system is made up of interfaces that interact with each other. The interfaces can be implemented by object-oriented code or by procedural code. Yukihiro Matsumoto, the inventor of Ruby, suggests that the interface is everything to the user . I would add that the interface is everything to the developer.

FINDING YOUR WAY IN THE DARK

We arrived at a camping area in the darkness of a new moon. We were looking for the campsite we had been assigned. We had a flashlight, but it yielded only a narrow beam of light. Relating what we saw in that narrow beam to the written directions was difficult.

The next morning, the sunlight revealed the big picture of the camping area. How the campsites were positioned in the area was apparent instantly. It would have been much easier had we had that big picture the night before.


Bertrand Meyer introduced the concept of Design by Contract in Object-Oriented Software Construction (Prentice Hall PTR, 2000). An interface has a contract with the user of that interface. The contract consists of preconditions and postconditions for every method in the interface. Preconditions are assertions that must be true when the method is called so that the method can perform its operations. Postconditions are assertions that should be true when the method finishes. [*]

[*] Meyer also introduces the invariant condition, which must always be true for a valid object. An invariant condition typically involves the state of the attributes of an object. An example is that the value of an attribute must always be between two values.

For example, when you're writing to a file, a precondition is that the file must be opened, and a postcondition is that the file length has changed (if you're appending to the end of the file). A precondition can also apply to the value of an argument in a call; for instance, it must be between a given set of values. The called method should assure that the postconditions are met. Otherwise , it has not performed its job properly. The questions are who checks the preconditions and what should result if a precondition is not met.

Some designers feel that it is the calling routine's responsibility to make sure the preconditions are satisfied. For example, the calling routine should ensure that parameters are within the ranges the preconditions specify. If they are not, the called method has no responsibility to perform its contractual obligation.

I prefer that the called method specifically check for its preconditions in its code. The code is the record of the contract. Putting checking responsibility on the called method parallels the concept that a whole program is responsible for checking the input from the user.

The called method contains the most knowledge of the preconditions. Unless performance requirements dictate otherwise, the called method can check for all preconditions and report via an error notification mechanism if the conditions are not satisfied. Is a failure to satisfy a precondition a programming error or an inherent error in the domain? That depends on context. The called method likely has the most information as to "what" caused the precondition violation, but the caller has the most information as to the meaning of the precondition violation.

Make the contract explicit. It should be visible in the code for the class and any derived documentation. To check the contract, you can use regular code style (e.g., if (parameter_value < 3) , assertions ( assert(parameter_value < 3) ), aspects (see http://aosd.net/ for details on aspects), or language-specific features.

CREATE INTERFACE CONTRACTS

Design with well-defined interfaces and enforce the contracts for those interfaces.


 <  Day Day Up  >  


Prefactoring
Prefactoring: Extreme Abstraction, Extreme Separation, Extreme Readability
ISBN: 0596008740
EAN: 2147483647
Year: 2005
Pages: 175
Authors: Ken Pugh

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net