Good Contract programming idiom?
bearophile
bearophileHUGS at lycos.com
Tue Mar 2 04:44:19 PST 2010
This is currently present in this Wikipedia page:
http://en.wikipedia.org/wiki/Class_invariant#D
It shows a normal way to use contract programming in D:
class Date {
private int day, hour;
invariant() {
assert(1 <= day && day <= 31);
assert(0 <= hour && hour < 24);
}
this(int d, int h) //constructor
in {
assert(1 <= d && d <= 31);
assert(0 <= h && h < 24);
}
out {
assert(day == d);
assert(hour == h);
}
body {
day = d;
hour = h;
}
public void setDay(int d)
in { assert(1 <= d && d <= 31); }
out { assert(day == d); }
body {
day = d;
}
public void setHour(int h)
in { assert(0 <= h && h < 24); }
out { assert(hour == h); }
body {
hour = h;
}
}
But generally public functions must test arguments in release mode too, so I (and lot of other people) don't like to use asserts in this situation. It's better to use an always present test followed by a throw (so such test is not in the precondition).
So this is the code that I prefer:
/// Thrown when an illegal argument is encountered.
class IllegalArgumentException : Exception {
this(string msg) {
super(msg);
}
}
class Date {
private int day, hour;
invariant() {
assert(1 <= day && day <= 31);
assert(0 <= hour && hour < 24);
}
this(int d, int h) //constructor
out {
assert(day == d);
assert(hour == h);
}
body {
if (d < 1 || d > 31)
throw new IllegalArgumentException("Wrong day.");
if (h < 0 || h >= 24)
throw new IllegalArgumentException("Wrong hour.");
day = d;
hour = h;
}
public void setDay(int d)
out { assert(day == d); }
body {
if (d < 1 || d > 31)
throw new IllegalArgumentException("Wrong day.");
day = d;
}
public void setHour(int h)
out { assert(hour == h); }
body {
if (h < 0 || h >= 24)
throw new IllegalArgumentException("Wrong hour.");
hour = h;
}
}
void main() {
Date d = new Date(/*d=*/10, /*h=*/5);
d.setHour(30); // throws
}
What do you think? Do you agree that's better to use exceptions like this to test arguments in public methods (instead of using asserts in preconditions)?
I have had to define locally that IllegalArgumentException because I think it's missing still in Phobos2 core.exception.
Those /*d=*/ /*h=*/ are useful because D doesn't support named arguments yet.
Bye,
bearophile
More information about the Digitalmars-d
mailing list