• No results found

An operation contract documents how a method can be used and what limitations it has. You should begin specifying this contract during analysis, fi nish the specifi cation during design, and then docu- ment the contract in your code, particularly within the header fi les. In this way, programmers who use your code can understand what contract they need to honor for the method to generate correct results. There is certain information you need to provide for each method. A method’s interface will specify how to call the method and the number, order, and types of arguments it expects—as already discussed. You also need to specify what should be true before the method is called and what will be true after the method fi nishes execution.

During design, it is also important that you clearly specify not only the purpose of each module, but also the data fl ow among modules. For example, you should provide answers to these questions for each module: What data is available to the module before its execution? What does the module assume? What actions have taken place, and what does the data look like, after the module executes? Thus, you should specify in detail the assumptions, input, and output for each module.

For example, if you as program designer needed to sort an array of integers, you might write the following specifi cations for a sort function:

The function will receive an array of num integers, where num > 0. The function will return the array with the integers sorted. Write specifi cations

for each module before implementing it

FIGURE 1-1 The task sort is a module separate from the MyProgram module

Data sorted into ascending order Unorganized data

MyProgram

(does interesting things) Sort his data for me;

I don't care how you do it

sort function

(sorts into ascending order)

A module’s contract specifi es the module’s purpose, assumptions, input, and output

Specify the data fl ow among modules

Specifi cations 7

First-draft specifi cations You can view these specifi cations as the terms of a contract between your function and the module

that calls it.

This contract helps programmers understand what responsibilities the module will have to the other modules in the solution. Whoever writes the sort function must live up to this contract. After the sort function has been written and tested, the contract tells the rest of the program how to call the sort function properly, as well as the result of doing so.

Notice, however, that a module’s contract does not commit the module to a particular way of performing its task. If another part of the program assumes anything about the algorithm, it does so at its own risk. Thus, for example, if at some later date you rewrite your function to use a differ- ent sorting algorithm, you should not need to change the rest of the program at all. As long as the new function honors the terms of the original contract, the rest of the program should be oblivious to the change.

This should not be news to you. Although you might not have explicitly used the term con- tract before, the concept should be familiar. You write a contract when you write a function’s pre-

condition , which is a statement of the conditions that must exist at the beginning of a function, as

well as when you write its postcondition , which is a statement of the conditions at the end of a function. For example, the sort function that adheres to the previous contract could appear in pseudocode1 as

// Sorts an array.

// Precondition: anArray is an array of num integers;num > 0.

// Postcondition: The integers in anArray are sorted.

sort(anArray, num)

These particular pre- and postconditions actually are defi cient, as can be the case in a fi rst-draft contract. For example, does sorted mean ascending order or descending order? How large can num be? While implementing this function, you might assume that sorted means ascending order and that num will not exceed 100. Imagine the diffi culties that can arise when another person tries to use sort to sort an array of 500 integers into descending order. This user will not know your assumptions unless you documented them by revising the contract as follows:

// Sorts an array into ascending order.

// Precondition: anArrayis an array of numintegers and 1 <= num<= MAX_ARRAY,

// where MAX_ARRAY is a global constant that specifi es the maximum size ofanArray .

// Postcondition: anArray[0]<= anArray[1]<= … <= anArray[num - 1];

// num is unchanged.

sort(anArray, num)

When you write a precondition, begin by describing the method or function’s input arguments, mention any global named constants that it uses, and fi nally list any assumptions that it makes. When you write a postcondition, describe what changes the module has made. Note that in the case of a method or function that returns a value—which is technically a part of the postcondition—the value should be described.

Novice programmers tend to dismiss the importance of precise documentation, particularly when they are simultaneously designer, programmer, and user of a small program. If you design sort but do not write down the terms of the contract, will you remember them when you later implement the function? Will you remember how to use sort weeks after you have written it? To refresh your memory, would you rather examine your program code or read a simple set of pre- and postcondi- tions? As the size of a program increases, good documentation becomes even more important, regard- less of whether you are sole author or part of a team.

Specifi cations are the terms of a contract

An operation contract should not describe how a module will perform its task Operation contracts should include precise preconditions and postconditions

1 Pseudocode in this book appears in blue.

Revised specifi cations

Precise documentation is essential