|
|
|
Programming by contract: preconditions and
postconditions |
|
|
|
|
An elaboration of precondition and
postcondition. |
|
Programming by contract. |
|
Test plans. |
|
|
|
|
A programming style in which the invocation of a
method is viewed as a contract between client and server, with each having
explicitly stated responsibilities. |
|
To imply a precondition, we will use the term ‘require:’. |
|
To imply a postcondition, we will use the term ‘ensure:’. |
|
|
|
|
|
|
Delineate, clearly and explicitly,
responsibilities between client and server. |
|
Delineate, clearly and explicitly,
responsibilities between the user of a method and the implementor of the
method. |
|
Ensure that any possible run time error will be
detected with minimal explicit error checking. |
|
Unfortunately, Java requires all checking to be
coded explicitly as part of the method implementation. |
|
|
|
|
|
|
Test for every possible error condition only
once (for program efficiency). |
|
Achieve a balance between program reliability
and maintainability. |
|
|
|
|
|
|
If the preconditions are satisfied, then the
server guarantees that the postconditions will be satisfied when the method
completes. |
|
If the preconditions are not satisfied, that is,
if the client does not meet his end of the contract, then the server
promises nothing. |
|
|
|
|
|
|
Constructor: |
|
require: |
|
hitStrength >= 0 |
|
stamina >= 0 |
|
ensure: |
|
this.name() == name |
|
this.location() == location |
|
this.strength() == strength |
|
this.stamina() == stamina |
|
|
|
|
|
|
Method takeHit: |
|
require: |
|
hitStrength >= 0 |
|
ensure: |
|
this.stamina() <= old.stamina() |
|
|
|
“old” refers to the value of the variable at the
beginning of the method; it is not a Java construct. |
|
|
|
|
|
We could set the value to a default value if it
doesn’t meet criteria. But this
isn’t entirely satisfactory, since it treats an error condition as a
normal, expected, occurrence. |
|
Usually an error is returned if the
preconditions are not met. |
|
CSCI.utilities package contains functionality
for doing this. |
|
|
|
|
|
Include the following statement immediately
after the package statement. |
|
import CSCI.utilities.*; |
|
Now you can use the method condition in the Require
class. |
|
static public void condition |
|
(boolean precondition) |
|
|
|
|
public Explorer (String name, |
|
rooms.Room location, |
|
int hitStrength, |
|
int stamina) { |
|
Require.condition(hitStrength >= 0); |
|
Require.condition(stamina >= 0); |
|
playerName = name; |
|
room = location; |
|
strengthPoints = hitStrength; |
|
staminaPoints = stamina; |
|
} |
|
|
|
|
Occasionally, preconditions constrain the order
in which methods can be invoked or require that an object be in a certain
state before a given method can be invoked. |
|
Example: an automobile must be running before it can move. |
|
|
|
|
|
Query postconditions: |
|
Queries do not change objects’ states. |
|
Query postconditions simply say something about
the value returned. |
|
Command postconditions: |
|
Commands change states. |
|
Command postconditions describe the new state
after execution of the command. |
|
Constructor postconditions: |
|
describe the initial state of the object. |
|
|
|
|
|
|
Since preconditions and postconditions are part
of the specifications, they should not mention private implementation
components. |
|
The reset method. |
|
ensure: |
|
tally == 0 (This is not correct!) |
|
|
|
|
|
|
Use named constants rather than literals in
preconditions and postconditions. |
|
public int suit ( ) |
|
ensure: |
|
result == Card.CLUB || |
|
result == Card.DIAMOND || |
|
result == Card.HEART || |
|
result == Card.SPADE. |
|
|
|
|
|
Test the implementation to insure that it
conforms to the specifications. |
|
Create a “test harness” to interact with the
object. |
|
The test system acts as the client of the
object: |
|
Invoke the object’s methods. |
|
Examine the behavior of the object. |
|
|
|
|
|
|
|
|
|
|
Provide an initial value for the object’s stamina. |
|
Invoke the takeHit method repeatedly. |
|
After each invocation, query the object with the
method stamina and report the result. |
|
|
|
|
|
|
|
|
|
|
Sample test session: |
|
Initial stamina value: 100 |
|
Stamina is now 100. |
|
Strength of hit: 10 |
|
Stamina is now 90. |
|
Strength of hit: 20 |
|
Stamina is now 70. |
|
Stength of hit: 5 |
|
Stamina is now 65. |
|
… |
|
|
|
|
|
|
|
Develop a test plan, giving values to be tested,
the purpose of the test and the individual test cases, and the expected
results. |
|
Test the system thoroughly, but not
inordinately. Explicitly include
limiting values and “equivalence classes.” |
|
If the system performs properly for one member
of an “equivalence class,” it should perform properly for all members of
the “equivalence class.” |
|
|
|
|
|
|
|
|
|
|
|
Purpose: test the stamina property of a Explorer
object, as modified by the method takeHit. |
|
Preconditions: |
|
Initial value of stamina >=0 |
|
hitStrength argument to takeHit >=0 |
|
Postconditions: |
|
stamina >=0 |
|
|
|
|
Test run 1: |
|
Input |
|
Initial stamina value: |
|
100 |
|
hitStrength: |
|
10 |
|
20 |
|
0 |
|
10 |
|
50 |
|
10 |
|
10 |
|
|
|
|
Test run 2: |
|
Input |
|
Initial stamina value: |
|
50 |
|
hitStrength: |
|
100 |
|
|
|
Test run 3: |
|
Input |
|
Initial stamina value |
|
0 |
|
hitStrength |
|
10 |
|
|
|
|
|
Programming by contract. |
|
The client satisfying the precondition. |
|
The server satisfying the postcondition. |
|
Test plans. |
|
|
|
|