Title: Design by Contract
1Design by Contract
- CSSE 514 - Programming Methods
- 4/5/01
2Lecture Outline
- Design by contract
- Class specification
- class invariants
- pre and post conditions
- Subcontracting and inheritance
- Quick overview of iContract
3Reference
- Bertrand MeyerObject-Oriented Software
Construction2nd editionPrentice-Hall 1997 - Chapters 11, 12.1-2 , 16.1
- http//www.eiffel.com/doc/manuals/technology/contr
act/page.html - Eiffel syntax is used
- should be easy to follow
- http//www.reliable-systems.com/tools/iContract/iC
ontract.htm
4Design by Contract
- For detailed design of classes
- Ideas developed from formal methods
- Hoare, Mills
- VDM (Vienna Development Method)
- Z, B (by Jean-Raymond Abriel)
- state-based specification
- Application to OO developed by Bertrand Meyer
- designer of Eiffel
5Specification of Classes
- State information represented by
- query functions
- with no side effects!
- boolean valued functions i.e.predicates
- class invariants express constraints on state
- Behavioural information
- operations (queries and updates)
- specify via effect on state
- preconditions
- postconditions
6Class Invariants
- Constraints on the state of the object being
modelled - specifies valid states of an object
- Need an idea of when objects are in a stable
state - not stable during method execution
- objects may be in states not satisfying the class
invariant - this may still be OK
7Stable Object States
- An object should be in a stable state
- in between external calls
- but what's external?
- in Java and C
- approximate this to be all calls to public
methods/functions - An object must satisfy its class invariant when
it is stable
- Typically
- after object construction
- before and after external calls
- Other methods may leave object in intermediate
states - should not be visible externally
- should be treated as internal helper functions
8Operational Specification
- Preconditions
- specify the applicability of operations
- state required before an operation
- Postconditions
- effect of operations
- relation between the states of an object before
and after an operation
9Class Contracts
- Class specification establishes a contract
between the client of a service offered by a
class and its supplier (the implementer) - A class specification conveys responsibilities
and benefits - A class specification provides more detail on the
externally observable behaviour than do type
declarations
10Class Contracts Assertions
- A class specification provides logical assertions
- Some or most of these assertions may be
computable - can be embedded in code
- provide run-time checks of system validity
- yields robust systems
- Particularly useful for debugging
- assertions should be switchable
- may be turned off in production release
11Contracts - class invariants
- Responsibility for implementer
- object creation must establish the invariant
- operations must maintain the invariant
- Benefit for implementer
- can assume invariant holds before operations
- Benefit for designer
- need not know all operations
- provides constraints even when incomplete
- partial designs with extensibility!
- Benefit for client
- know what the object is
- what constraints it operates under
12Contracts - preconditions
- Responsibility for client
- ensure precondition is satisfied before applying
an operation - NOTE this!!
- design by contract implies that clients must
check preconditions
- Benefit for implementer
- only consider states where precondition (with
class invariant) holds - simplifies code
- less redundant error checking code
- Benefit for designer
- defer error handling
13Contracts -postconditions
- Responsibility for implementer
- ensure each operation establishes postcondition
- Benefit for client
- know what theyre getting
- not how theyre getting it
- Benefit for designer
- defer error handling
14Design by Contract vs Defensive Programming
- Defensive programming
- popular software engineering credo
- often recommended
- Key idea
- every software component should protect itself as
much as possible
- BUT this leads to
- redundancy in error checking
- no defined responsibility
- client or supplier?
- premature error handling in code
- Design by contract
- defines the error checking responsibility
- simplifies implementation
15What the Client Needs
- Design by contract requires a client to satisfy
the precondition - To do this
- the client must know what the precondition is
- be able to check or establish all parts of the
precondition - The supplier must provide this capability in the
interface
16Case Study --- Tennis Tournament Scoring System
- Detailing a Game class
- start with Game CRC card
- identify methods for class
- type signatures
- specification
17GAME CRC Card
- who is winner, loser
- who is server, receiver
- know point winners
- manage points
- COMPETITOR
- COMPETITOR
- POINT
- POINT
18Identifying the Methods
- Game
- -- queries
- competitors
- score
- isNew, isOver
- winner, loser
- -- updates
- add
19Commentary
- Initially only consider external interface
- Avoid redundant features at first
- unless they simplify specification
- may be added for clients later
- for example server, receiver, serviceBreak
- Avoid object creation routines at first
- tend to evolve with design
- may depend on concrete data representation
- test for newly created games with new
20Method Type Signatures
- interface Game
- -- queries
- SetltCompetitorgt competitors()
- int score(Competitor)
- boolean isNew()
- boolean isOver()
- Competitor winner()
- Competitor loser()
- -- updates
- void add(Point)
21Specifying the contract
- Class invariant captures logical constraints on
object states by relating the behaviour of query
functions - In specifications, variables may be quantified
- may not be directly expressible in programming
language
- Here we use
- Java/C like expressions
- use require for the precondition
- use ensure for the postcondition
- For writing contracts in UML, use
- Object Constraint Language
- OCL
22Specifying the contract
- Constraints on queries
- either in the class invariant
- or in post-condition for that query
- class invariant offers freedom for store vs
compute decisions
- Guiding principle
- constraints fundamental for describing allowable
object states - belong in class invariant
- queries with preconditions must be guarded if
used in class invariant - constraints defining results of queries
- belong in postcondition
23Preconditions for Game
- Game
- -- queries
- int score(Competitor x)
- require(competitors.contains(x))
- Competitor winner()
- require(isOver())
- Competitor loser()
- require(isOver())
24Preconditions for Game
- Game
- -- updates
- void add(Point p)
- require(
- !isOver()
- p ! NULL
- p.isOver()
- p.competitors() competitors()
- )
25Commentary on Preconditions
- This is not real Java code!
- Absence of preconditions
- equivalent to True
- method is always applicable
- obligation is on the implementer!
- as for isNew and isOver
- precondition for score says
- can only query the score of a competitor who is
in the game - an alternative for the last part of the add
precondition would be - p.game() this
26Class Invariant for Game
- Game
- boolean invariant()
- return
- competitors().size() 2
- (isOver() implies
- score(winner()) gt score(loser())
- )
-
-
27Commentary on Class Invariant
- There are 2 distinct competitors in a game
- When the game is over, the winner is the
competitor with the greater score - this constraint could be part of the
postcondition of score(), or winner(), or loser()
- Implies is a boolean operator
- in Java or C p implies q would be defined as
!p q
28Postconditions for Game
- Game
- -- queries
- int score(Competitor x)
- ensure(result gt 0)
-
- Competitor winner()
- ensure(competitors.contains(result))
-
- Competitor loser()
- ensure(competitors.contains(result))
-
29Postconditions for Game
- Game
- -- updates
- void add(Point p)
- ensure(
- !isNew()
- competitors() old competitors()
- score(p.winner())
- old score(p.winner()) 1
- score(p.loser()) old score(p.loser())
- )
-
30Commentary on Postconditions
- result is a standard name for the return value of
the query - old prefixing an expression refers to the value
of the expression at the start of the operation - effect of add(point) is to increment the score
for the point winner
- The non-negative score constraint is logically
redundant for the given set of operations - it does effectively constrain future update
operations - may be better in class invariant
31Commentary on Specification
- Behaviour has not been completely constrained
- rules for a GAME being over have not been
stated - Specification of GAME may be specialized
- either a tie-break
- or a normal game
- No inheritance specified yet
- wise to avoid until features have been detailed
32Commentary on Specification
- type signature indicates where responsibilities
lie - if add was given no arguments
- GAME itself would have been responsible for the
control of the next point - the current design leaves the control open
- responsibilities are spelt out in even more
detail with the class contract - compromise between
- flexible designs and
- maintaining consistency
33Commentary on Specification
- GAME specification imposes some requirements on
POINT - must at least have the methods
- isOver winner loser competitors
- So
- detailing the contract of a class
- leads to detail for one of its suppliers
34Subcontracting and Inheritance
- Following subcontracts must be accepted by a
subclass for it to be a valid subtype - class invariant may not be weakened
- specialization subsetting
- pre-conditions may not be strengthened
- inherited operations must retain their
applicability - post-conditions may not be weakened
- inherited operations must retain their effect
35Rationale
- we will assume that x.f() establishes the
postcondition of A.f() - but execution of B.f() will establish the B.f()
postcondition - this must imply the postcondition for A.f()
- B is a subclass of A
- x is a variable of type A
- We reason about the function call
- x.f()
- the precondition of A.f() must hold
- but with dynamic binding we may apply B.f()
- so precondition of A.f() must imply the
precondition of B.f()
36Example of Weakened Precondition
- class FixedAspectRectangle
- float base()
- float height()
- void stretch (float xScale, float yScale)
- require (
- xScale yScale,
- xScale gt 0.0
- )
- ensure (
- base() old base() xScale
- height() old height() yScale
- )
-
- boolean invariant() return base() gt 0.0
height() gt 0.0
37Example of Weakened Precondition
- class Rectangle extends FixedAspectRectangle
- void stretch (float xScale, float yScale)
- require (
- xScale gt 0.0
- yScale gt 0.0
- )
- // same postcondition
-
-
- it is clear that
- if FixedAspectRectangle.stretch(x,y) is
applicable, with its stronger precondition - then so too is Rectangle.stretch(x,y)
38iContract
- Implemented as a Java preprocessor
- process your Java code with iContract
- produces a set of decorated Java Files
- compile the decorated code with regular Java
compiler - directives in Java code reside in class and
method comments - like JavaDoc directives
39Preconditions
- In iContract, you place preconditions in a method
header using the _at_pre directive. Here's an
example -
- / _at_pre f gt 0.0 / public float
sqrt(float f) ...
40Postconditions
- Postconditions are likewise added to the header
comment of the method they belong to. In
iContract, the _at_post directive defines
postconditions - / _at_pre f gt 0.0 _at_post Math.abs((return
- return) - f) lt 0.001 / public float
sqrt(float f) ... -
41Invariants
- With iContract, you can specify invariants in the
header comment of a class definition - / A PositiveInteger is an Integer
- that is guaranteed to be positive.
_at_inv intValue() gt 0 / class PositiveInteger
extends Integer ...