4

I'd heard about Design by Contract a long time ago and always was confused by this question. The approach uses real-world client-supplier analogy to describe caller-callee relationships. It stays, that if a client ensures preconditions before calling a supplier, the supplier will get the benefits by avoiding preconditions check; and, on the other hand, if supplier ensures postconditions, the client will get benefit by avoiding checking those; everyone is happy. That's what is written in Wikipedia, on EiffelSoftware website and in billions of other places.

Example

But. The world is cruel. And in most cases in the real world everyone prefers to check what he got for work instead of being sure that the work is done correctly. Even in the example above, we are checked in the airport for the preconditions (ticket, baggage, etc), the supplier doesn't trust us. And in the end of the fly, we want to be sure that we're really in Chicago and our baggage hasn't been lost.

So, what the benefits of checks made by a client instead of a supplier? I see no difference. Is the analogy with the real world is bad or did I just missed something? Could we swap checkers of postconditions and preconditions and still call it design by contract?

  • IMHO the analogy is bad. They simplified it a lot by adding trust between client and airline. In the real world there is no such thing, and the airline has its own obligations and reputation. In a software project however, trust between 2 modules can and does exist and fail-fast behavior is acceptable. If 2 modules trust each other and one passes around garbage in the arguments to the other, it's fine to die with an exception (or not, it depends). If someone finds you took a knife on board, it's not fine to boot you out of the plane mid-flight. – Ordous Oct 5 '16 at 17:35
6

The reason that preconditions should be checked by the client is that the client is in a better position to collect or solicit any missing information because (presumably) it is better able to communicate with the user. By the time the supplier gets involved, all it can do is throw an error and abort processing, since it doesn't know where any missing information should come from.

Postconditions should be handled by the supplier to alleviate the effort required to write a client. If the client is guaranteed that (for example) a particular value will never be null, or will only contain values within a specified range, it enables the client to simply process the values instead of having to validate them first.

|improve this answer|||||
  • Thank you, that is what I was thinking about. Anyway, I think there should be a better analogy :) – neoascetic Oct 5 '16 at 18:07
4

Like most analogies it isn't universally applicable. It's really meant to give you permission not to feel you must always check everything at every step. However, it's not saying that if the check has been done once it should never be done again.

Between those extremes is where trust comes in. If you never expect to be called by anything else trusting your caller may make sense. If you're a library, trusting your caller is insane.

You seem to be advocating the library attitude of never trusting your caller. The benefits are assurance of valid input and easy to decipher error messages. The cost, in increasing order of significance, is redundant checks impacting: performance, code writing, and code reading. The worst is conceptual.

Thinking of every object boundary as a library boundary comes at a cost. I don't design interfaces the same across a library boundary as I do within the application. It's the same with validation.

If all your objects lead interesting lives the library attitude is valid. But if I have a quite little object that's only used by this one caller I trust you'll forgive me for assuming some checks, and some work, have already been done.

It's also worth noting that each object should be concerned with it's own responsibilities. It's my callers job to give me what I need. But not to decide what I need. Thus it's not reasonable for the airplane to expect the taxi to check the size and weight of your bags the same way the airport would. These are different relationships.

|improve this answer|||||
  • "I don't [validate] the same across a library boundary as I do within the application" Do you typically formalize the basis for the trust, i.e., with automated testing of the components' outputs? – jscs Oct 5 '16 at 18:27
  • 2
    I do. Outside layers taking input should validate rigorously. But never so much that they must predict inner layer state or behavior. When you step on the plane they don't pat you down. They do want to know you paid enough for this flight. If I throw exceptions I want a unit test that validates that exception is thrown when it should be. So that means I throw bad data at the app. I just don't throw it at parts that should never see it. – candied_orange Oct 5 '16 at 18:38
  • I think I understand. You test that the Client in the example has baggage under the required 50 lb, but not the Supplier's reaction to 65 lb of baggage. – jscs Oct 5 '16 at 18:45
  • 1
    @JoshCaswell: The better analogy is that a check is made that a 65lb bag gets rejected at the check-in counter, but the weight is not re-checked every time the bag gets placed on a conveyor belt in the luggage handling system. Then it is just assumed to be within limits. – Bart van Ingen Schenau Oct 6 '16 at 6:56
  • 2
    @BartvanIngenSchenau correct but I think I can do you better. Look at size. There is coupling is between the check-in counter and the overhead bin. The check-in counter has a test for size (a little box you can put your bag in). It is meant to prevent an oversized error in the cabin. This only works because there is trust and a standard size for overhead bins on every airline. On the other hand the taxi does a size test as well but you shouldn't trust the fact that your luggage fit's in the trunk as meaning anything to the airline. That's why I'm saying not all boundaries are the same. – candied_orange Oct 6 '16 at 7:05
3

You're correct. The analogy isn't a particularly good one. That may be part of the difficulty with the idea of design by contract: it's so rare in real life to be able to wholly rely on the contract that both parties almost always do the pre/post/invariant checks themselves.

The critical idea in a design by contract language is that, if you can state the conditions, the compiler can enforce them. This has the benefit that they only need to be done once as well as the, possibly more important, benefit that they will definitely be done.

|improve this answer|||||

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.