Contract Programming in D
If you are a defensive programmer like me, you make heavy use of assertions to guard assumption you make in your code. For example, a method for adding an order item to an order object could look something like this:
class Order
{
private List orders;
int addItem(OrderItem item)
{
assert(assigned(item));
item.order = this;
assert(assigned(items));
items.add(item);
assert(items.count > 0);
return items.count;
}
}
This style of programming adds some clutter to the code, but makes the program more robust and reliable over time.
The D Programming Language have built in support for contract programming and I have been curios to see if that can be an alternative to my defensive programming style. At first look it seems to be a close match. Both techniques allow you to make safe assumptions in business logic code. The difference is where you put your defensive code.
In contract programming, or Design by Contract as it was originally called, there are the concepts of pre- and postconditions, conditions that are expected to be met on the entrance to and on the exit from methods. Additionally you have the concept of class invariants, which asserts a certain state before and after (but not during) a method call.
So, a transformed contract programming version of my defensive style example above could look something like this:
class Order
{
private List orders;
invariant { assert(assigned(items); }
int addItem(OrderItem item)
in { assert(assigned(item)); }
out {assert(items.count > 0);}
body
{
item.order = this;
items.add(item);
return items.count;
}
}
This may not seem like less clutter but it does two important things: First it separates the defensive code from the business logic. Pre- and postconditions are neatly placed in in- and out-blocks, while business logic dwells in the body-block.
Secondly, general assertions that may need to be checked in every method of the object (like checking that the orders list in the above example is assigned), are handled in in place: the invariant block. Nice and DRY.
It seems like I could use Contract Programming for the same purposes as the normal defensive programming technique, but there are a couple of issues that keep me from taking the step:
- I’m not sure how preconditions and postconditions are affected in an override scenario. The language specification says that preconditions are OR’ed together, meaning that if one precondition passes the others are ignored. My own tests show a different behavior, but I need to take a closer look to be sure.
- Contract Programming and Normal Defensive programming are conceptually two very different things: Contract Programming, like the name suggests, are taking place in between the programming interfaces of objects, while the assertion defensive style is more general. You can say that Contract Programming defends the Program Design against abuse, while the defensive programming style defends the implementation against unexpected events.
- Contract Programming moves the defensive code away from the code that benefits from its protection. This could become a maintaining problem.
I currently feel that Contract Programming should be used only in the context for which it was created: big projects with many developers, where a massive (not agile) design phase precedes an equally massive phase of implementation. But, I’ll probably use class invariants to DRY up my general asserts where applicable.
I think another potential issue is how to document the rules/contracts, and how to check they are present and as intended.
I’m currently working in Java, and code defensively – and the checks end up being documented in the Javadoc, in natural language. And they need tests to prove they behave as intended and documented (though I guess many wouldn’t bother).
With “contract” programming, because the conditions are more formally identified and defined, I’d expect that a lot of that should disappear or be handled automatically (or at least benefit from being clearly separate and more codified).
You definitely have a point there.
In my experience, the need of documentation varies depending on situation. If a defensive code is solely internal, testing class internal, non-published implementation specific code, then there is little need for documentation and specific tests. On the other hand, if it is protecting from external misuse, then you need to document and test your assumptions. In those cases I tend to make as little assumptions as possible.
Contract Programming tends to be self documenting, and tools can be made to help extracting information from them. That is an advantage DbC has over the informal defensive coding.
Preconditions may be loosened, while postconditions may be tightened. The individual conditions aren’t ORed, it’s the entire in clause that’s ORed between super and sub classes for a particular method. I’ve not used D, but this is the same concept used by other languages that support DbC. It would be interesting to see code that you think exhibits different behavior.
DbC doesn’t fully replace defensive coding. There’s a reason why you still have the assert(). You should really be using both, since assert() can’t do everything that DbC can do, and vice versa. However, if all of your code is properly contracted, you’ll find fewer instances where you need an assert().
wekempf: I meant the entire clause, not the individual conditions. I wasn’t clear in my post and I’m glad you clarified it.
Stay tuned, I’m currently investigating the behavior of D:s contracts in an override scenario. I hope to post the results and the code later today.