Design by Contract (DBC)

DBC is a software engineering practice typically used in #202202041514 and #202207072153 that treat module design as designing a contract. The contract consists of three major conditions: precondition, postcondition and class invariant.

Precondition(s) is what must be true in order for the routine to be called. This can be translated to the routine’s requirements. For example: What should be the type of the parameter(s)? What value of the parameter(s) is expected to be passed in? The precondition(s) of a routine should be checked by the caller, that it is the responsibility of the caller to pass valid data to the routine. If such requirement can’t be satisfied, then the routine shall never be called.

Postcondition(s) is what the routine is guaranteed to do. It is usually about verifying the return state of the routine. For example: What should be the type of value returned by the routine? Could the return value smaller than the said value? This should be checked by the routine itself. However, the checking of the postcondition can be varying due to the language implementation as it relies on the parameters passed into the method. If the routine allows the change of such parameter, the contract can be circumvented, which violates the DBC principle.

Class invariant is a condition that remained unchanged to constrain the objects of the class. It must always be true before and after the call of the method. The class invariant can be violated in between the call, though.

There are other invariants that could further enhance the design of the codebase. Loop invariants can be used to make sure that the condition or state of a variable or an object is true before entering the loop and on each loop iteration. Semantic invariants are more like a philosophical contract. It applies inviolate requirements which express the meaning of a thing instead of restricting which policy should be employed.

Note: The old value of a parameter is important on forming the contract.

If any of these violated during the compile time or runtime, the building of the program should fail or the program simply crashed. The alternative to this is to raise an #exception when such violations are encountered. Then, the programmer will handle them in care and crash the program gracefully if possible.

With DCB, the program will do no more and no less than it claims to do. The contract should be put on the base class, and to be enforced to all its deriving classes. The new subclass must support the same method, and that method retains its meaning without much alteration (invariant must be preserved stated by #SOLID). It can, however, accept a wider range of input (precondition can be weakened) or make a stronger guarantee (postcondition can be strengthened) than its parent.

The programmer should be strict about what should they accept before the beginning of the function or method implementation, and promise as little as possible in return. Such mindset will help to construct a good contract for the function or method to follow. Additionally, contracts must be designed before committing to code implementation, just like setting up the test facilities.

Example of implementation of DBC are:

  • Eiffel (a programming language focus on DBC) 1
  • GNU Sather (a programming langauge focus on DBC) 2
  • Nana (library for #c and #cpp) 3
  • iContract (library for #java)
  • Cofoja (modern library for #java) 4
Links to this page
#test #oop #debugging #literate-programming #exception #c #cpp) #java)