Why design by contract can be usefulFiled Under: Weekly Tuesday Dose of goodness
Hi all,
This week I’ll be talking about something more related directly to development. As you might have seen, this week’s topic is about design and contract. In Java, it’s used as DBC (Design by contract) where your javadoc serve as a certain checkpoint for values passed in.
We’re not going to talk about language specifics here. The focus will be on design and contract itself.
Why am I talking about design by contract now? Cause I slapped myself in the face once hard enough to warrant a post.
Introduction
Let’s begin with my incident this time round. It was a routine class, in fact, a simple Axis-aligned bounding box class which has several methods to conduct collision detection against other boundaries.
For the benefit of those who doesn’t understand what’s an AABB, let me just explain a few bits of it. First of all, it contains 2 vectors. In computers, 3D maths vectors usually consists of 4 numbers instead of 3 and on top of that, they’re 32-bits floating point numbers.
So one would expect a vector type to be x, y, z, w. (With w being the homogenous value)
An AABB (Axis-Aligned Bounding Box) will have 2 of such vectors. Namely the minimum and maximum vectors. But don’t get them wrong. They’re not wholly vectors. Rather, they’re more like positions that indicate the max and min boundaries of a box.
So, in order to start things up, I’ll normally initialize both vectors to zero, in which making all x,y,z,w to 0.0f respectively.
However, this approach is wrong.
While making things zero is a technical habit, I’ve inadvertently neglected the design constraints and contract. How so? How is it possible that I’m wrong by setting things to zero? After all, the compiler will not set it for me.
Disaster Scenario
Well you see, let’s simply the problem a little. Let’s just assume that the whole exercise is on nothing but to sort out 2 numbers. Instead of vectors, we’ll have just 2 floating point numbers representing max and min respectively.
Next, we initialize them to zero.
After which, we pass in a value to a method, say,
void someClass::contribute(float value)
{
if(value > this->max)
this->max = value;
if(value < this->min)
this->min = value;
}
Very simple piece of source code that can be easily understood by most. It simply assess the argument and see if it establishes any new limits or not. If yes, then the min or max will be updated accordingly.
Now, my default max and min are 0 as of now.
However, my values are 21 and 53 only and by design only. That means to say, there’ll only be 2 numbers passed into contribute(). My expected outcome would therefore be:
min = 21
max = 53
WRONG!
Look carefully, both numbers are now 0. By assuming zero, I’ve also assumed the fact that a smaller number will always be one that is smaller than zero. That’s completely wrong.
My results here therefore will be:
min = 0
max = 53
Here begins a bug that will easily spiral across the application via cascading chain effect and the rest is disaster.
Therefore, the fix would simply be:
1) Initialize the class to the smallest or biggest number always
2) Pass in whatever numbers that needs to be sorted afterwards
So in this fix, I’ll start with:
min = 21
max = 21
Then I’ll contribute 53 in my next call to contribute() and thus:
min = 21
max = 53
What Could Have Been Done?
There’re more than 1 ways to tackle this issue.
But the cheapest and easiest way out of course would be to first design a set of constraint and contract for the class.
This set of constraints will determine what are the possible range of values that will be allowed to go through the class and thus to its attributes.
The contract will determine how things are done in the processes and procedures. Thus specifying its workability range clearly.
These 2 would have easily helped me identify this simple bug much earlier, but no. Because it was technically simple, I assumed that it was design-simple as well.
That was a huge mistake.
In the end, the bug took me 3 days to figure out and you couldn’t imagine how stupid I felt when I realised that this was the cause.
The other way to tackle this issue is to approach the design constraints by regression testing using automated tools and data-driven testing approach to drive a set of data through my class and verify its expected value.
A testing tool would have certainly helped in this case.
A simple assertion to the return value or state of the object would have identified this mistake in no more than 3 hours.
That’s right. I’d have saved myself up to 23 times the amount of time I took based on my normal debugging process by going the design by contract ways.
(24×3 = 72 hours, if 3 hours can replace 72 hours, savings are 69 hours, which is exactly 23 times)
Conclusion
Finally, I’d like to say many times as I’ve said before to myself and all who are reading my articles:
DESIGN FIRST, IMPLEMENT LAST
Don’ts
- Don’t rush into coding.
- Don’t underestimate the requirements.
- Don’t despise simple logic.
- Don’t fix a bug without understanding the design.
Do’s
- Know what you are doing really
- Know what you intend to do
- Know your design constraints
- Know your design contract
With that, I confess my own stupidity and my own foolishness for not following my very own advice.
Lesson learnt. The HARD way.
Hope that will help you folks out there to be more vigilant and concerned about your design in your codes. Don’t let design issues turn your job into a nightmare!
Signing off,
Jeremy
- Permalink
- Admin
- 25 May 2010 9:55 AM
- Comments (0)