Formal specification
A formal specification of the behavior of an object may
be given by defining a pre-condition
and post-condition for each method.
The pre-condition of a method specifies in a logical
manner what
restrictions the client invoking a particular method
is obliged to comply with.
When the client fails to meet these requirements
the result of the method will be undefined.
In effect, after the violation of a pre-condition
anything can happen.
Usually, this means that the computation
may be aborted or that some other means of error-handling
may be started.
For instance, when the implementation language
supports exceptions an exception handler may be invoked.
The post-condition of a method states what
obligations the server object has when executing the
method, provided that the client's request satisfies
the method's pre-condition.
Apart from specifying a pre-condition and post-condition
for each method publicly supported by the class,
the designer of the class may also specify a class invariant,
to define the invariant properties of the state of
each instance of the class.
A class annotated with an invariant
and pre- and post-conditions for
the methods may be regarded as a contract,
since it specifies precisely (in an abstract way)
the behavioral conformance conditions of the object
and
the constraints imposed on the interactions
between the object and its clients.
See slide [3-obligations].
Contractual obligations
| client | supplier |
pre-condition | obligation | benefit |
post-condition | benefit | obligation |
slide: Contractual obligations
Assertions -- formal specification
- require -- method call pre-condition
- ensure, promise -- post-condition
- invariant -- object invariance
slide: Formal specification of contracts
Intuitively, contracts have a clear analogy
to our business affairs in everyday life.
For instance, when buying audio equipment, as
a client you wish to know what you get for
the price you pay, whereas the dealer may require
that you pay in cash.
Following this metaphor through, we see that
the supplier may actually benefit from
imposing a (reasonable) pre-condition and that
the client has an interest in a well-stated post-condition.
Most people are not willing to pay without knowing
what they will get for their money.
The use of contracts was originally
proposed by [Meyer88], and is directly
supported by the language Eiffel,
which offers the keywords require
(to indicate a pre-condition),
ensure (to indicate a post-condition)
and invariant (to indicate the invariance condition).
See slide [3-formal].
The Eiffel environment has options to dynamically
check any of the three kinds of assertions,
even selectively per class.
The assertions, except for the invariance condition,
are directly embedded in the code.
Although less elegant, the same functionality
can be achieved in C++ by using the assert
macro defined in {\tt assert.h} as explained
in section [ASSERT], which also introduced
the require and promise macros for C++.
For dynamically checking the invariance condition,
a test should be executed when evaluating the
constructor
and before and after each method
invocation.
While a method is being executed, the invariant
need not necessarily hold,
but it is the responsibility of a method to
restore the invariant when it is disrupted.
In case object methods are recursively applied,
the invariant must be restored when returning
to the original caller.
An alternative approach to incorporating assertions
in a class description is presented in [Cline],
which introduces an extension of C++ called Annotated C++.
Instead of directly embedding assertions in the
code, Annotated C++ requires the user to
specify separately the axioms characterizing
the functionality of the methods and their
effect on the state of the object.
Important from a design perspective is
that the specification, even without an implementation
of the methods, may be understood as specifying
a contract.
In practice, they may only be stated as comments.