Question

I've recently had a few scenarios where small changes to code have resulted in changing preconditions across multiple classes and I was wondering if design by contract is supposed to be that way or not.

public Goal getNextGoal() {
    return goalStack.pop();
}

If goalStack.pop() has a precondition that the stack isn't empty, then does getNextGoal() need to explicitly have the same precondition? It seems like inheriting the preconditions would make things brittle, and changing to a queue or other structure would change the preconditions to getNextGoal(), it's callers, and it's callers' callers. But it seems like not inheriting the preconditions would hide the contracts and the callers, and the callers' callers, wouldn't know about the preconditions.

So brittle code where all callers know and inherit the preconditions and postconditions of the code they call, or mysterious code where callers never know what the deeper preconditions and postconditions are?

Was it helpful?

Solution

It depends on what your calling method does exactly. The important thing with preconditions is that the caller is responsible for fulfilling the preconditions.

So if callers of your GetNextGoal method should be responsible for providing a non-empty stack, then you should indeed also set preconditions on your GetNextGoal method. Clarity of preconditions is one of the huge advantages of Code Contracts, so I'd suggest you put them in all places where callers have to fulfill the preconditions.


If your code seems brittle however, it might be a sign that you need to refactor some code.

It seems like inheriting the preconditions would make things brittle, and changing to a queue or other structure would change the preconditions to getNextGoal(), it's callers, and it's callers' callers.

If you expose the queue to the callers and change it later ( to another structure, like you said ), it's callers would also have to change. This is usually a sign of brittle code.

If you would expose an interface instead of a specific queue implementation, your preconditions could also use the interface and you wouldn't have to change the preconditions every time your implementation changes. Thus resulting in less brittle code.

OTHER TIPS

Exceptions are one solution but perhaps not feasible for your situation.

Documenting what happens if there are no goals is normal.E.G. This is what malloc() does in C

I can't tell if you are using Java or C++ or something else as each language might have slightly more natural ways for that specific language.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top