Você está na página 1de 47

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

Advanced Programming By Contract: Designing for Correctness

TOOLS Pacic, 1999 Melbourne, Australia

1 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

Contact Info
James C. McKim, Jr. CIS Department Rensselaer at Hartford jcm@rh.edu Much of the work presented here was done in collaboration with Richard Mitchell, University of Brighton.

2 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

Overview
What is Programming by Contract? How do we use Programming by Contract to design correct classes? Cashier example. Fully checkable contracts: The power of Recursion Reductionism and Nondeterminism Conclusion Appendix

3 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

What it is
Object Oriented Systems are made up of Objects. An Objects behavior is specied by a Class. Classes consist of Features. A Feature is either a Command or a Query or both. A command may change the state of an object, while a query returns information about the state of the object.

4 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

What it is (Contd)
Programming by contract is a means of rigorously specifying what a feature provides. In particular you can use it to: 1. Specify when it is appropriate to call a feature (Precondition). 2. Specify what a feature accomplishes when it is called correctly (Postcondition). Collectively, preconditions and postconditions are called assertions.

5 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

What It Is (Contd)
If class A uses the services of class B we say A is the client of the supplier B. The contract between the client and the supplier may be stated as: The supplier guarantees the postcondition of any feature the client invokes, provided the client guarantees the precondition.

6 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

Example
class STACK[ G ] top : G require depth > 0 depth : INTEGER capacity : INTEGER push( x : G ) require depth < capacity ensure top = x ; depth = old depth + 1 ...

7 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

Example
class CLIENT stk : STACK[STRING] some_feature is -- need to push Mary onto the stack but were -- unsure if theres room. if stk.depth < stk.capacity then stk.push( Mary ) else -- alternative action end

8 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

How to Use It
Principle 1: Separate commands and queries. Discussion: You must at least provide some pure queries to have a chance at runtime checkability. Justication: Separation of concerns. Violations: If speed is of the essence (e.g. in a hard real time system) it may be necessary to combine commands and queries in some instances. Example: Stack

9 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

How to Use It (Contd)


Principle 2: Do not be limited by your language. For example feel free to use universal and existential quantiers as need be. Justication: The goal is a complete specication that is readable by technical people. Its nice if the specication is compilable, but its not necessary. Example: in class SET[ G ] the following assertion for_all x : G (not has( x )) states that the set has no elements i.e. its empty.

10 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

How to Use It (Contd)


Principle 3: Identify a basic specication set of pure queries. Every other feature in the class must be completely dened in terms of the queries in the specication set. Justication: Provides a controlled means of rigorously specifying many features in terms of a few. Once the specication set is understood, clients can quickly comprehend any feature in the class.

11 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

How to Use It (Contd)


class SET[ G ] make( c : INTEGER ) cardinality, capacity : INTEGER insert( x : G ) delete( x : G ) is_empty : BOOLEAN is_full : BOOLEAN has( x : G ) : BOOLEAN

12 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

How to Use It (Contd)


-- specication set: has, cardinality, capacity cardinality, capacity : INTEGER has( x : G ) : BOOLEAN make( c : INTEGER ) require c >= 0 ensure cardinality = 0 ; capacity = c ; for_all x : G (not has(x))

13 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

How to Use It (Contd)


insert( x : G ) require not has( x ) ; cardinality < capacity ensure has( x ) ; cardinality = old cardinality + 1 capacity = old capacity ; for_all y : G (y/=x implies has( y ) = old has( y )) delete( x : G ) require has( x ) ensure not has( x ) ; cardinality = old cardinality - 1 capacity = old capacity ; for_all y : G (y/=x implies has( y ) = old has( y ))

14 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

How to Use It (Contd)


Note: Including has( x ) and not has( x ) in the preconditions of insert and remove, respectively is debatable. Can we remove them?

is_empty : BOOLEAN ensure Result = (cardinality = 0) is_full : BOOLEAN ensure Result = (cardinality = capacity)

15 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

How to Use It (Contd)


We can add new commands merely by expressing their effects on the specication set. expand( c : INTEGER ) require c > capacity ensure cardinality = old cardinality ; capacity = c for_all x : G (has(x) = old has( x )) contract( c : INTEGER ) require c < capacity ; c > cardinality ensure cardinality = old cardinality ; capacity = c for_all x : G (has(x) = old has( x ))

16 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

Cashier Example
The CASHIER class is supposed to model a supermarket cashier in a simulation of the supermarket. Customers always enter the cashier line at the end of the line, but may leave from anywhere. However it is assumed that the customer currently being served will not leave until completion of service. It is part of CASHIERs responsibilities to stamp each Customers arrival and departure time.

17 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

Cashier Example(Contd)
-- specication set: customer, length, clock length : INTEGER clock : CLOCK customer( i : INTEGER ) : CUSTOMER require i >= 1 ; i <= length make( c : CLOCK ) ensure length = 0 ; clock = c

18 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

Cashier Example(Contd)
insert( c : CUSTOMER ) -- New customer arrives ensure length = old length + 1 ; c.start_time = clock.time customer( length ) = c ; clock = old clock for_all i, 1..old length (customer( i ) = old customer( i )) remove( i : INTEGER ) -- ith customer leaves. require i >= 2; i <= length ensure length = old length - 1 ; clock = old clock for_all j, i..length (customer( j ) = old customer( j+1 )

19 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

Cashier Example(Contd)
nish -- Finish processing current customer. require length >= 1 ensure length = old length - 1 ; clock = old clock old customer( 1 ).nish_time = clock.time for_all i, 1..length (customer( i ) = old customer( i+1 )) current_customer : CUSTOMER require length >= 1 ensure Result = customer( 1 ) no_customers : BOOLEAN ensure Result = (length = 0 )
20 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

Fully Checkable Contracts


Not easy to come by with todays technology. If we take a top, depth, and capacity as the specication set for a STACK, then we can fully specify push. push( x : G ) require depth < capacity ensure depth = old depth + 1 ; top = x ; capacity = old capacity

21 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

Fully Checkable STACK


But pop is not so easy. pop require depth > 0 ensure depth = old depth - 1 ; capacity = old capacity top = ??????

22 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

Fully Checkable STACK (Item)


One solution is to add an item query and use universal quantiers. The specication set is now item, depth, and capacity, and the relevant parts of the specication follow. item( i : INTEGER ) : G require i >= 1 ; i <= depth -- No ensure clause as this is a specier. top : G -- no longer a specier, so dene it. require depth > 0 ensure Result = item( depth )
23 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

Fully Checkable STACK (Item)


push( x : G ) require depth < capacity ensure depth = old depth + 1 ; capacity = old capacity item( depth ) = x for_all i, 1..depth - 1 (item( i ) = old item( i )) pop require depth > 0 ensure depth = old depth - 1 capacity = old capacity for_all i, 1..depth (item( i ) = old item( i ))

24 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

Fully Checkable STACK (Item)


Advantages: Easy to read, complete specication. Disadvantages: Checkable by few, if any, existing languages. Note the problem of dening old in the scope of the quantier. Is this still a STACK?

25 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

Fully Checkable STACK (Contd)


For an alternative, consider the mathematical, ADT, denition of a Stack. new -> empty push( x ) -> not empty pop( push( x ) ) is the identity function top( push( x ) ) = x

26 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

Fully Checkable STACK (Contd)


Proofs that implementations of Stacks accurately represent the above, involve denitions of equality of stack objects and require either recursion or induction. Can we write the specication in such a way that recursion will provide the checkability for us? We will need to dene equality and we will need queries that produce stacks so we can verify equality at appropriate times.

27 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

Fully Checkable STACK (Contd)


In particular well need to be able to refer to a new stack thats equal to the current one with: 1. One additional element, the pushed element. 2. One less element, the popped element.

28 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

Fully Checkable STACK (Contd)


minus_top( x : G ) : STACK[ G ] require not is_empty -- This will be a specier, so no postcondition is necessary. plus_top( x : G ) : STACK[ G ] ensure not Result.is_empty Result.top = x Result.minus_top.is_equal( Current )

29 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

Fully Checkable STACK (Contd)


is_equal( other : STACK[ G ] ) : BOOLEAN require other /= Void ensure Result = (is_empty and other.is_empty) or -- Both are empty, or ((not is_empty and not other.is_empty) and then -- Neither is empty and (top = other.top and -- Tops are the same, and minus_top.is_equal( other.minus_top) )) -- Everything else is the same.

30 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

Fully Checkable STACK (Contd)


Basic Speciers are is_empty, top, and minus_top. is_equal is an Auxiliary Specier. That is, its dened in terms of the Basics, but its convenient to use it to specify other features. The signicant features can now be fully specied as follows: make ensure is_empty

31 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

Fully Checkable STACK (Contd)


push( x : G ) ensure not is_empty ; top = x ; minus_top.is_equal( old clone( Current ) ) pop require not is_empty ensure is_equal( old minus_top ) depth : INTEGER ensure is_empty implies Result = 0 not is_empty implies Result = minus_top.depth + 1
32 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

Fully Checkable STACK (Contd)


We have a complete, fully checkable, specication using todays technology. If we allow ourselves universal quantiers, we can include some interesting, though not checkable, invariants. for_all x : G (not plus_top( x ).is_empty) for_all x : G (plus_top( x ).top = x) for_all x : G (is_equal( plus_top( x ).minus_top))

33 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

Fully Checkable Classes


Many other data structures, particularly the linear ones, will yield to the same techniqes. As will variations of these, such as CASHIER. Left as an exercise. An interesting nonlinear structure is SET.

34 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

SET Revisited
Note that while we included cardinality as a basic specier earlier, arguably we shouldnt need it. If we can answer the has question in every case, we know everything there is to know about the Set. In fact, if we think about set operations such as union, intersection, and difference, we quickly see that it is not always an easy task to specify how cardinality changes.

35 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

SET Revisited (Contd)


Wed like to treat cardinality similarly to the way we treated depth in the STACK class. But this requires some distinguishable element of the Set to play the role of top. We choose to call this element choice. This element may be nondeterministically chosen, however, it must be a pure query.

36 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

SET Revisited (Contd)


Since choice is an arbitrary nondeterministic element of the set, we need not specify its precise value. choice : G require there_exists x : G (has( x )) -- not empty ensure has( Result ) -- nondeterministic Note: Depends only on has.

37 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

SET Revisited (Contd)


Just as choice is the analog of top, well need minus_choice as the analog of minus_top. minus_choice : SET[ G ] require not is_empty ensure not Result.has( choice ) for_all x : G (choice /= x implies (has( x ) = Result.has( x ))) Note: Depends on has, choice, and is_empty.

38 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

SET Revisited (Contd)


Basic speciers: has Auxiliary speciers (1): choice, is_empty Auxiliary speciers (2): minus_choice Sample features follow: has( x : G ) : BOOLEAN -- Basic specier is_empty : BOOLEAN ensure Result = for_all x : G (not has( x ))

39 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

SET Revisited (Contd)


make ensure is_empty cardinality : INTEGER ensure is_empty implies Result = 0 not is_empty implies Result = 1 + minus_choice.cardinality

40 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

SET Revisited (Contd)


is_equal( other : SET[ G ] ) require other /= Void ensure Result = for_all x : G (has( x ) = other.has( x )) merge( other : SET[ G ] ) require other /= Void ensure for_all x : G (has( x ) = (old has( x ) or other.has( x ))) Note that theres no need to mention cardinality in either of the above.
41 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

SET Revisited (Exercise for the Reader)


Is there a way to make better use of choice and minus_choice so that we can use recursion to reduce the use of quantiers thus providing better checkability? Answer: Yes, but its messy. See appendix.

42 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

Conclusion
Rigorous specications provide complete documentation useful both for coding and testing, and so are a good thing. Programming by contract is a way to provide rigorous specications in a way that is accessible to a good technical programmer. Therefore Programming by Contract is a good thing!

43 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

Appendix: Some hints on specifying Set in a checkable fashion


Try to mimic the recursive spec of Stack as best you can. Basic specication set becomes: choice, minus_choice, and is_empty.

44 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

Appendix (Contd)
We can dene even something as basic as has from these: has( x : G ) : BOOLEAN ensure is_empty implies not Result not is_empty and choice = x implies Result not is_empty and choice /= x implies Result = minus_choice.has( x )

45 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

Appendix (Contd)
In the case of the Stack, the intuitive notion of equal Stacks matched the specication set. That is, the two stacks being compared needed to agree on is_empty, top, and minus_top. In the case of the Set, the intuitive notion of equality is has the same elements, and any notion of choice is irrelevant. We probably need a second notion of equality that matches the specication set, is_specically_equal(?).

46 of 47
Tools Pacic, 1999 jcm@rh.edu

A d v a n c e d

P r o g r a m m i n g

b y

C o n t r a c t

Appendix (Contd)
And well need more layers of speciers. Heres what my version has. Basic specication set: is_empty, choice, minus_choice Auxiliary specication (1): is_specically_equal, has Auxiliary specication (2): plus, specic_clone, is_subset_of Auxiliary specication (3): minus, is_equal

47 of 47
Tools Pacic, 1999 jcm@rh.edu

Você também pode gostar