420 likes | 532 Views
Contracts and Invariants. Section 6.2 (JIA’s). Design by Contract --- DBC. Each class interface defines a set of services via its public methods The declaration of a method what the user cares about in the API or javadoc documentation pages defines only the type and not behavior
E N D
Contracts and Invariants Section 6.2 (JIA’s)
Design by Contract --- DBC • Each class interface defines a set of services via its public methods • The declaration of a method • what the user cares about in the API or javadoc documentation pages • defines only the type and not behavior • Behavior = what happens when method is run • When can I call it? What happens if I do? • One should not write a class without a formal contract • lists the internal consistency conditions that the class will maintain (the invariants) • for each method • the correctness conditions that are the responsibility of the client (the precondition) • and those which the operation promises to establish in return (the postcondition) • Missing contracts • Silence on some aspects of behavior • Multiple interpretations • Contradictions
Design by Contract --- DBC • Methods have preconditions and postconditions; classes have invariants • “When you call me, the following must be true” • “If the preconditions are satisfied, I guarantee the following will be true when I return” • Thus the two make up a contract: If you do this, I promise to do that”
Pre and Post Conditions • A precondition is a Boolean expression that must hold when the method is invoked • i.e. a method may not be invoked when the preconditions is false • preconditions are guaranteed by the caller • A postcondition is a Boolean expression that must hold when the method invocation returns • postconditions are guaranteed by the callee
Contract of a Method • Documented using special tags (most tags are not supported by JavaDoc) • /** • * JAVADOC Documentation • *@pre precondition • *@post postcondition • */ • public … someMethod(){} • Those tags may occur multiple times for any single method • in such cases, the conjunction of all Boolean expression would serve as the pre and post conditions
Contract of a Method • The following special tags are used in pre- and post-conditions • @resulta variable holding the return value of a method • @nochangea Boolean expression implying that the state of the object is not changed by the method • in postconditions for accessors • Can also use the following operators • logical implication (a b is true iff a is false or both are true) • logical equivalence (ab is true iff both are either true or false) … can == instead
Example 1 • /** • * Returns the number of elements in • * an ArrayList • * • * OTHER JAVADOC TAGS • *@pre true • *@post @result==list.size() • *@post @nochange//no change for the state of the object (i.e. an accessor or a getter method) • */ • public int size(){}
Example 2 • /** • * Returns true iff the list is empty • * • * OTHER JAVADOC TAGS • *@pre true • *@post @result == (size() <= 0) // this is a condition so that is why we use == instead of = • *@post @nochange • */ • public boolean isEmpty (){}
Example 3 • /** • * Returns the 1st element in the list • * • * OTHER JAVADOC TAGS • *@pre !isEmpty() • *@post @result == list.get(0) // this is a condition so that is why we use == instead of = • *@post @nochange • */ • public Object head(){…} • Last()?
Example 4 • /** • * Returns the last element in the list • * • * OTHER JAVADOC TAGS • *@pre !isEmpty() • *@post @result == list.get(size()-1) • *@post @nochange //no change for the state of the object (i.e. an accessor or a getter method) • */ • public Object last()
Mutators • A mutator is a method that changes the state of the object (setter) • @post @nochange for accessors • Need to distinguish between the state of the object before and after the method invocation • values of expressions in the postcondition are evaluated w.r.t. the after state of the object (i.e. after method returns) • Add a new item at top and return it • @post @result == list(size()-1) • To refer to object immediately before the method is invoked, we use the pre-state notation • expression@pre • Add a new item to list increase size • E.g. @post size() == size()@pre + 1
Collections • Contracts involving a collection of objects (i.e. list, vector, array, etc … ) often require quantified expressions • Universal quantification • Holds on every object in the collection • @forall x: [Range]@Expression • Existential quantification • Holds on at least one object in the collection • @exists x: [Range]@Expression • x is a variable over Range • Range specifies the collection of objects • Expression is a Boolean expression
Specifying Ranges • [m..n] • Where m and n are integer expressions • @post @forall k:[0..size()-1)]@SOME_COND • ClassName • Defines range of all instances of the class • @post @forall k:CAR@SOME_COND • Expression • Evaluates to a collection like a set, bag, list, etc … • @post @forall k:{“Red”,”Green”,”Blue”}@SOME_COND
Example 5 • /** • * Inserts a new element at the ith position only if item is not * null • * • * OTHER JAVADOC TAGS • *@pre item !=null && i>=0 && i<size() • *@post size() == size()@pre + 1 • *@post @forall k:[0..size()-1]@ • * ((k<i list.get(k) == list@pre.get(k))&& • * (k==i list.get(k) == item)&& • * (k>i list.get(k) == list@pre.get(k-1))) • */ • public void insert(Object item, int i) • Try • public void insertHead(Object item) //i.e. at location 0 • public void insertTail(Object item) //i.e. at location size()-1
Example 6 • /** • * Inserts a new element at the head only * if item is not null • * • * OTHER JAVADOC TAGS • *@pre item !=null • *@post size() == size()@pre + 1 • *@post @forall k: [0..size()-1]@ • * ((k==0 list.get(k) == item) && • * (k>0 list.get(k) == list@pre.get(k-1))) • */ • public void insertHead(Object item)
Example 7 • /** • * Inserts a new element the tail only if • * item is not null • * • * OTHER JAVADOC TAGS • *@pre item !=null • *@post size() == size()@pre + 1 • *@post @forall k: [0..size()-1]@ • * ((k<size()-1 list.get(k)==list.get@pre(k)) && • * (k==size()-1 list.get(k)==list.get(size()-1)) • */ • public void insertTail(Object item) • Try • public Object remove(inti) • public Object removeHead() • public Object removeTail()
Example 8 • /** • * Remove and return the element at position * index • * • * OTHER JAVADOC TAGS • *@pre !isEmpty() && index >=0 && index <size() • *@post size() == size()@pre-1 • *@post @result == list@pre.get(index) • *@post @forall k: [0..size()-1] @ • *((k< index list.get(k)==list@pre.get(k))&& • *(k>= index list.get(k)==list@pre.get(k+1))) • */ • public Object remove(int index)
Example 9 • /** • * Remove and return the Head item • * • * OTHER JAVADOC TAGS • *@pre !isEmpty() • *@post size() == size()@pre-1 • *@post @result == list.get(0)@pre • *@post @forall k: [0..size()-1] @ • * (list.get(k) == list@pre.get(k+1)) • */ • public Object removeHead()
Example 10 • /** • * Remove and return the Tail item • * • * OTHER JAVADOC TAGS • *@pre size() > 0 • *@post size() = size()@pre-1 • *@post @result == list.get(size()-1)@pre • *@post @forall k: [0..size()-1] @ • * (list.get(k)==list@pre.get(k)) • */ • public Object removeTail()
Invariants of Classes • Two states for objects • Objects in the transient state • Being manipulated • i.e. 1 or more MUTATOR methods of its class are being executed • Objects in the stable state • Constructed • Not being manipulated by a MUTATOR • A class invariant is a condition that always applies on any object of the class whenever it is a stable condition • An object is said to be a in well-formed state if all class invariants hold
Invariants of Classes • /** • * JAVADOC DESCRIPTION • * @invariant Expression • */ • public class AClass { • //.. • } • Where the Expression is a Boolean expression • @ invariant tag could occur multiple times • Conjunction of all needs to be taken
Example 1 • /** • * JAVADOC DESCRIPTION • * @invariant (age > 0 && age < 150) • * @invariant (balance >= 0) • */ • class Account { • private String name; • private String acct#; • private int age; • private double balance; • private void deposit(…) { /* ... */ } • public void withdraw(…) { /* ... */ } • }
Example 2 • Class that maintains a sorted intlist[] • /** • *@invariant @forall x: [0..size()-2] @list[x] <= list[x+1] • */
Example 3 • class MyStack { • private Object[] elems; • private int top, max; // top = index of last array element + 1 (0 initially) // max = maximum array capacity • public MyStack(int size) { • top = 0; • max = size; • elems = new Object[size];} • public boolean isFull() { • return top == max;} • public boolean isEmpty() { • return top == 0;} • public void push(Object obj) { • elems[top++] = obj;} • public Object pop() { • return elems[--top];} • } // End MyStack
/** * @invariant (top >= 0 && top <= max) */ class MyStack { private Object[] elems; private int top, max; /** @pre (size > 0) @post (top==0 && max == size && elems!=null) */ public MyStack(int size) { top=0 max = size; elems = new Object[size];} /** @pre true @post (@result (top == max)) @post @nochange */ public boolean isFull() { return top == max;} /** @pre true @post (@result (top == 0)) @post @nochange */ public boolean isEmpty() { return top == 0; } /** @pre !isFull() @post (top == top@pre + 1) @post @forall k:[0..top-1]@ ( (k==top-1 elems[k]==obj) (k<top-1 elems[k]==elems@pre[k])) */ public void push(Object obj) { elems[top++] = obj;} /** @pre !isEmpty() @post (top == top@pre - 1) @post @result == elems[top@pre-1]@pre @post @forall k:[0..top-1]@ elems[k] == elems@pre[k] */ public Object pop() { return elems[--top];} } // End MyStack Example 3
Assertions • Used to perform run-time checking of method preconditions, postconditions and class invariants • For Preconditions • At entry point of each method • Defensive programming • For Postconditions: • At exit point of each method (before returning) • Assists in unit testing and debugging • For Invariants: • boolean well_formed() method • At exit point of every constructor • At entry and exit point of every public mutator method • Acessors?
Assertions • A Boolean condition given at a location of program • Should be true whenever the flow of execution reaches that location • assert Assertion_Condition; • E.g. assert top < max • Has no effect if the assertion is true • Throws an AssertionError exception if false (Unchecked) • Run with –ea option • java –ea MyStack
Example 1 • /** • * Returns the head of the list • * @pre !isEmpty() • * @post @result == element(0) • * @post @nochange • */ • public Object head(){ • assert well_formed(); //for invariant • assert !isEmpty(); • Object result = element(0) • assert result .equals(element(0)); • assert well_formed(); //for invariant • return result; • }
Example 2b • /** • * Inserts a new element at the ith position • * • * OTHER JAVADOC TAGS • *@pre item !=null && i>= 0 && i<size() • *@post size() = size()@pre + 1 • *@post @forall k:[0..size()-1)]@ • *(k<i list.get(k) == list@pre.get(k))&& • *(k==i item == list.get(k))&& • *(k>i list.get(k) == list@pre.get(k-1)) • */ • public void insert(Object item, int i){ • …}
Assertions • Postconditions that deal with prestates are more difficult to assert • Need to save the values of objects or variables whose prestates are required • Manual … very tedious • Clone objects • The cloned object’s class must implement Cloneable • Point p1 = new Point(); • Point p2 = (Point) p1.clone(); • Different than copying? • Point p2 = p1; • Section 6.3.4 JAI’s
Copying vs. Cloning • Point p1 = new Point(); • Point p2 = p1; • Point p1 = new Point(); • Point p2 = (Point) p1.clone();
Cloning • When you invoke clone(), it should either: • throw CloneNotSupportedException • If the object doesn't implement the Cloneable interface • return an Object reference to a copy of the object upon which it is invoked, • If the object implements the Cloneable interface • all the fields initialized to values identical to the object being cloned • In class Object, the clone() method is declared protected • If all you do is implement Cloneable, only subclasses and members of the same package will be able to invoke clone() on the object • To enable any class in any package to access the clone() method, you'll have to override it and declare it public, as is done next
Cloning • In class Object • protected Object clone () throws CloneNotSupportedException • When you invoke clone(), it should either: • throw CloneNotSupportedException • If the object doesn't implement the Cloneable interface • checked exception • return an Object reference to a copy of the object upon which it is invoked • If the object implements the Cloneable interface • all the fields initialized to values identical to the object being cloned
class CoffeeCup implements Cloneable { • private int innerCoffee; • public void add(int amount) { • innerCoffee += amount; } • public int releaseOneSip(int sipSize) { • int sip = sipSize; • if (innerCoffee < sipSize) { sip = innerCoffee; } • innerCoffee -= sip; • return sip; } • public int spillEntireContents() { • int all = innerCoffee; • innerCoffee = 0; • return all; } }
make a copy of class CoffeeCup • class Example1 { • public static void main(String[] args) { • CoffeeCup original = new CoffeeCup(); • // Original contains 75 ml of coffee • original.add(75); • CoffeeCup copy; • try{ copy = (CoffeeCup)original.clone(); } • Catch(CloneNotSupportedExceptioncnse) { … } // Copy now contains 50 ml of coffee • copy.releaseOneSip(25); • intorigAmount = original.spillEntireContents(); • intcopyAmount = copy.spillEntireContents(); • System.out.println("Original has " + origAmount + " ml of coffee."); • System.out.println("Copy has " + copyAmount + " ml of coffee."); } }
Cloning In General • In class Object, the clone() method is declared protected • If all you do is implement Cloneable, only subclasses and members of the same package will be able to invoke clone() on the object • To enable any class in any package to access the clone() method, you'll have to override it and declare it public
class CoffeeCup implements Cloneable { • private intinnerCoffee; • public Object clone() { • try{ return super.clone();} • Catch(CloneNotSupportedException …){ …} } • public void add(int amount) { • innerCoffee += amount; } • public intreleaseOneSip(intsipSize) { • int sip = sipSize; • if (innerCoffee < sipSize) { sip = innerCoffee; } • innerCoffee -= sip; • return sip; } • public intspillEntireContents() { • int all = innerCoffee; • innerCoffee = 0; • return all; } }
make a copy of class CoffeeCup • class Example1 { • public static void main(String[] args) { • CoffeeCup original = new CoffeeCup(); • original.add(75); // Original contains 75 ml of coffee • CoffeeCup copy = (CoffeeCup)original.clone(); • copy.releaseOneSip(25); // Copy now contains 50 ml of coffee • int origAmount = original.spillEntireContents(); • int copyAmount = copy.spillEntireContents(); • System.out.println("Original has " + origAmount + " ml of coffee."); • System.out.println("Copy has " + copyAmount + " ml of coffee."); } }
Objects that Contain Other Objects • Object's clone() will copy the value of each instance attribute from the original object into the corresponding instance variables of the copy object • Values of primitive attributes are copied • Values of reference attributes are duplicated • Shallow copying vs. deep copying • If one of those variables is an object reference, the copy object will get a duplicate reference to the same object • Immutable (e.g. String or object with no mutators) • Constant
Primitive data types in Java: integer (byte, short, int, & long), floating-point ( float & double), character (char) and boolean (boolean) Immutable and constant objects
class CoffeeCup implements Cloneable{ • private Coffee innerCoffee = new Coffee(0); • public Object clone() • { • try{ • CoffeeCup copy = (CoffeeCup) super.clone(); • //clone attributes of reference type explicitly • copy.innerCoffee = (Coffee) innerCoffee.clone(); • return copy; • } • Catch(CloneNotSupportedException){…} • } • Classes that already implement Cloneable • http://java.sun.com/javase/6/docs/api/java/lang/class-use/Cloneable.html
Beyond Contracts • Writing assertions while programming • one of the fastest and most effective ways to detect and correct bugs • serve to document and enhance maintainability • Don’t use assertions for error handling (e.g. exceptions) • Assertions check for things that should never happen