|
Guidelines
Experience with more than 2.000 students and professionals show that there are some guidelines which help to avoid the most common traps with C4J. Guideline 1 : Set up your project as a maven project! The extend assertions concept by means of separating assertions into separate contract classes has one disadvantage: The number of sourcecode files you have to manage will double or triple as each target class has a corresponding class contract and/or interface with its interface contract.An approved way to deal with that complexity issue is to structure the project adhering to the directory structure of an idiomatic Maven project:
Guideline 2 : If you need local variables in a pre- or postcondition declare them inside the if statement body!
public class TimeOfDaySpecContract implements TimeOfDaySpec {
...
@Override
public void setHour(int hour) {
// method body sourcecode before the precondition if statement will be skipped
int thisIntWillBeSkipped;
thisIntWillBeSkipped = 1;
if (preCondition()) {
// this works fine
boolean localBooleanInPreConditionBody = false;
localBooleanInPreConditionBody = true;
...
}
// method body sourcecode between the precondition and postcondition if statements will be skipped
boolean thisBooleanWillBeSkipped;
thisBooleanWillBeSkipped = true;
if (postCondition()) {
// this works fine
double localDoubleInPostConditionBody = 0.0;
localDoubleInPostConditionBody = 1.23;
...
}
// methody sourcecode after the postcondition if statement will be skipped
double thisDoubleWillBeSkipped;
thisDoubleWillBeSkipped = 1.23;
}
}
Guideline 3 : If you need a stateful contract use private instance variables!
public class TimeOfDaySpecContract implements TimeOfDaySpec {
private boolean setHourHasBeenCalled;
@Override
public void setHour(int hour) {
...
if (postCondition()) {
...
setHourHasBeenCalled = true;
}
}
@Override
public int getHour() {
if (preCondition()) {
if (setHourHasBeenCalled) {
...
}
}
...
}
}
Guideline 4 : When using the old method do keep in mind that it returns a old reference to the object not a reference to a deep copy of the object!
public class TimeOfDaySpecContract implements TimeOfDaySpec {
...
@Override
public void setHour(int hour) {
...
if (postCondition()) {
...
assert unchanged(target.getMinute()) : "minute unchanged";
// recommended version:
// If minute is changed in the implemented setHour method, the assertion will definitely fail!
assert target.getMinute() == old(target.getMinute()) : "minute unchanged";
// variation 1 : does work as intended as getMinute() returns the basic type int
// i.e. if minute is changed in the implemented setHour method,
// the assertion fails as target.getMinute() != old(target.getMinute()).
assert target.getMinute() == old(target).getMinute() : "minute unchanged";
// variation 2 : does NOT work as intended as target is a reference type
// and therefore: target == old(target), so both references point to the same object!
// As a consequence target.getMinute() == old(target).getMinute() will always be true
// even if minute is changed in the implemented setHour method!
}
}
}
|