Title: Design by Contract
1Design by Contract
- Ranga Rodrigo based on Mark Priestley's Lectures
2Quiz Question 1
- State whether the following statements are true
or false. - Subclasses can safely strengthen invariants.
- Weakened preconditions in a subclass are implied
by the preconditions in the superclass. - Trying to write a stronger precondition has no
effect. - Strengthened preconditions in a subclass imply
the postconditions in the superclass. - An attempt to write a weaker postcondition in a
subclass has no effect.
3Quiz Soulution 1
- State whether the following statements are true
or false. - Subclasses can safely strengthen invariants.
- Weakened preconditions in a subclass are implied
by the preconditions in the superclass. - Trying to write a stronger precondition has no
effect. - Strengthened preconditions in a subclass imply
the postconditions in the superclass. - An attempt to write a weaker postcondition in a
subclass has no effect.
True
2 marks
2 marks
True
True
2 marks
False
2 marks
True
2 marks
4Contract
- A contract is a mechanism which specifies exactly
how an interaction---between people,
organizations etc---will take place. - The purpose of a contract is so that each party
knows exactly what they must do, and what they
can expect from the other party.
5Contract
- For example, a contract of employment specifies
- the hours an employee must work, the duties to be
carried out, the applicable codes of conduct etc.
i.e., the employee's responsibilities - the salary paid to the employee, holiday
allowance, pension arrangements etc. i.e., the
employer's responsibilities.
6Design by Contract
- A software design technique introduced by
Bertrand Meyer (the primary developer of Eiffel). - It applies to the specification of software
modules (classes, methods) the idea is that the
specification should be viewed as a contract
between the supplier and the client of the
module. - The supplier has the responsibility to provide
certain services (in the module) - The client has the responsibility to use the
module in certain ways - This can be viewed as a compile-time relationship
between the people writing the code, or a
run-time relationship between calling and called
routines.
7Design by Contract
- The supplier has the responsibility to provide
certain services (in the module). - The client has the responsibility to use the
module in certain ways. - This can be viewed as a compile-time relationship
between the people writing the code, or a
run-time relationship between calling and called
routines.
8Contract of a Routine
- For example, in the case of a routine the
supplier provides parameters when calling the
routine, which then carries out some operations
and possibly returns a value. - The obligations on the client (caller) can be
expressed as preconditions they define what must
be true before the routine starts. - The obligations on the supplier can be expressed
as postconditions they define what must be true
after the routine completes.
9- The contract is therefore
- If the client calls the routine in a situation
where the preconditions are true, the supplier
guarantees to complete the routine in such a way
that the postconditions are true.
10Interaction with Other Features
- The DBC mechanism interacts in various ways with
other language features - 1.Preconditions should be visible to the client,
so that clients can write code which ensures that
the preconditions are true before a routine is
called. This means that preconditions cannot
contain references to "private" features of the
class.
11Interaction with Other Features
- The same argument does not apply to
postconditions client code would not normally
check that a postcondition had been satisfied, so
postconditions can contain "private" data. (The
contract view in EiffelStudio displays "private"
postconditions, but not the private features they
refer to, which is a bit peculiar.) - In subclasses, contracts can be redefined by
weakening preconditions and strengthening
postconditions. - Notice that a class invariant is not really part
of a class's external contract.
12Defensive Programming
- Defensive programming is the idea that a module
should anticipate everything that could go wrong
and include code to handle exceptional
situations. - A divide routine coded defensively might look
like this
13safe_divide( top DOUBLE bottom DOUBLE )
DOUBLE is do if bottom 0 then
-- do some error handling
end Result top / bottom end
- DBC discourages this approach, which on the face
of it seems a bit odd given the emphasis placed
on secure programming.
14- The arguments in favour of the DBC view
- Unlike defensive programming, the use of a
precondition makes the client's responsibilities
explicit. - The supplier shouldn't have to compensate for
deficiencies in the client the whole idea of a
contract is that if the precondition is not met,
it's the client's fault. - In many cases, there is no sensible error
handling code that can be written in a routine
when its precondition is false. All that can be
done is to raise a run-time exception, and this
can more simply be done by an assertion checker
built in to the run-time system. This means that
the design of an exception handling mechanism is
closely related to the implementation of DBC.
15Exceptions in Java and C
- Java and C have very similar exception
mechanisms. Here is a simple Java program that
generates a run-time exception when an attempt is
made to divide by zero. (Exceptions are normally
said to be raised or thrown.)
16public class Zerodiv public static void
main(String args) int x 5 int y
0 System.out.println("Quotient "
quotient(x,y)) static int quotient(int
x, int y) return x/y
17- The output from this program is the following
Exception in thread "main" java.lang.ArithmeticExc
eption / by zero at Zerodiv.quotient(Zero
div.java12) at Zerodiv.main(Zerodiv.java
7)
18An Exception
- The exception is a combination of two things
- A run-time signal which is passed up the call
stack, interrupting the normal flow of control of
the program. - An object, of class java.lang.ArithmeticException,
defining the type of the exception, and possibly
containing some data relating to the failure.
19Exceptions can be caught, or handled, by
enclosing the relevant code in a try block
- Exceptions can be caught, or handled, by
enclosing the relevant code in a try block
static int quotient(int x, int y) try
return x/y catch (Exception e)
return 0
20Catch Statement
- The catch statement prevents an exception being
passed up the call stack, and allows the
programmer to provide alternative code to be
executed in the exceptional case. - An exception will be caught if its type conforms
to the type specified in the catch clause.
21Try Statement
- Any statement can be enclosed in a try block, so
the exception here could equally well be caught
in the main function as follows. - Notice that this code specifies the type of the
exception more carefully, and provides error
handling code that is more appropriate to the
situation.
22public static void main(String args) int x
5 int y 0 try
System.out.println("Quotient " quotient(x,y))
catch (ArithmeticException e)
System.out.println("Can't divide by zero")
23C and Java Mechanisms
- There are various refinements and complications
which are being ignored here, but the main
features of the C/ Java mechanism are
illustrated, namely - Exceptions are modelled as objects, of library or
user-defined types. - Any statement can be included in a try block, and
exceptions caught and handled at that point.
24C and Java Mechanisms
- This is therefore a very general mechanism for
transferring control and data between arbitrary
points in a program, which is being applied to
the specific problem of handling exceptions. - It raises the possibility of misusing the
mechanism to achieve arbitrary transfers (a bit
like the famous goto statement), to the detriment
of good program structure.
25Exceptions in Eiffel
- Eiffel, by contrast, defines a more restricted
notion of exception handling, which is meant to
capture only those aspects which are crucial to
the problem, and it expresses this syntactically. - It is therefore aiming to be more restrictive
than C and Java, but to provide a simple
mechanism which is more secure, in being less
open to misuse for other purposes.
26Exceptions in Eiffel
- In Eiffel, exceptions are related to the idea of
design by contract. - A routine call is said to succeed if it
terminates in a state which satisfies its
contract, and to fail otherwise. - An exception is an event which causes a routine
to fail, such as the following
27- Trying to call a feature on Void.
- Trying to assign Void to an expanded target.
- An abnormal condition being detected by the
hardware or operating system. - Calling a routine that fails (i.e., exceptions
are passed up the call stack, as in Java and
C). - An assertion not being satisfied, if the
appropriate check is being performed at run-time.
- Executing an explicit instruction to trigger an
exception.
28Legitimate Responses in Eiffel
- Java and C essentially allow a program to
respond in any way to an exception. - By contrast, Eiffel recognizes only two
legitimate responses - To retry the routine, presumably after making
some attempt to ensure that the exception will
not occur again. - To fail restore the environment to a stable and
legal state, and report failure to the calling
routine.
29- The Eiffel syntax for exceptions reflects this,
and is very simple
a routine can have a rescue clause containing
code which is executed if an exception occurs in
the body of the routine, and the rescue can
contain a retry statement, which causes the body
of the routine to be executed again from the
beginning
30- A rescue clause that does not include a retry
statement causes the exception to be passed up
the call stack. - This means that a routine is forced to satisfy
its contract or to fail you are not meant simply
to patch things up in the rescue clause before
carrying on. - This principle is meant to ensure that if a
routine finishes without an exception, the caller
can assume that it has met its contract (i.e.,
its postconditions are true).
31- As a result of these rules, it is difficult to
write Eiffel routines which do arbitrary things
like return zero when a division by zero is
attempted
quotient(x INTEGER y INTEGER) REAL is
local division_tried BOOLEAN do
if not division_tried then Result x /
y end rescue division_tried
True retry end
32- This returns zero if y is zero, because of the
automatic initialization of Result when the
routine is retried. - However, from the DBC point of view, this is a
strength, not a weakness. - The argument is that if an attempt is made to
divide by zero, there is mathematically no
correct answer that can be returned. If the
routine was equipped with pre and post
conditions
33quotient(x INTEGER y INTEGER) REAL is
require NonZeroDenominator y / 0
do Result x / y ensure
IgnoreRoundingErrors x Result y end
- an exception will automatically be raised if a
zero denominator is detected. - There is no value that could be returned that
would make the postcondition true in all cases,
and therefore no way that the routine can meet
its contract in this case. There is no
alternative but to return an exception.