4 Designing contracts

 

This chapter covers

  • Designing pre-conditions, post-conditions, and invariants
  • Understanding the differences between contracts and validation

Imagine a piece of software that handles a very complex financial process. For that big routine to happen, the software system chains calls to several subroutines (or classes) in a complex flow of information: that is, the results of one class are passed to the next class, whose results are again passed to the next class, and so on. As usual, the data comes from different sources, such as databases, external web services, and users. At some point in the routine, the class TaxCalculator (which handles calculating a specific tax) is called. From the requirements of this class, the calculation only makes sense for positive numbers.

We need to think about how we want to model such a restriction. I see three options when facing such a restriction:

4.1 Pre-conditions and post-conditions

4.1.1 The assert keyword

4.1.2 Strong and weak pre- and post-conditions

4.2 Invariants

4.3 Changing contracts, and the Liskov substitution principle

4.3.1 Inheritance and contracts

4.4 How is design-by-contract related to testing?

4.5 Design-by-contract in the real world

4.5.1 Weak or strong pre-conditions?

4.5.2 Input validation, contracts, or both?

4.5.3 Asserts and exceptions: When to use one or the other

4.5.4 Exception or soft return values?

4.5.5 When not to use design-by-contract

4.5.6 Should we write tests for pre-conditions, post-conditions, and invariants?

4.5.7 Tooling support

Exercises

Summary