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! } } } |