Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
Software validation: Design by Contract Defensive Programming A dynamic technique: Detect failures Defensive Programming • Assertions • if (age<=0) then /* handle error */ else i = 1/age; public void method(Object arg) { if arg==null …} Provide appropriate exception handlers Invariants • Example: Pre-/postconditions • Incorporate checks wherever possible In object-oriented programming: Design by Contract (B. Meyer, 1985) CE202 & CE654 Software Engineering, Spring term 2010–11 1 Dr Amnon H. Eden, School of Computer Science and Electronic Engineering, University of Essex Objects as Service Suppliers “Contract” Communicating objects take two roles: 2 The purpose of the contract is Supplier Client double sqrt(double arg) { // arg >= 0 // ... // Calculate & return square root of sqrt // |result*result–arg| < 0.001 } How this differs from documentation? Contract 3 To render the agreement explicit To avoid “misunderstandings” To trace pinpoint (clarify) violations of the agreement Cannot be checked mechanically Inconsistent with the code Usually ignored 4 1 Structure of a Contract Two sides to a contract: Example: Square Root double sqrt(double arg) { // ... // Calculate & return square root of sqrt } The Client: The necessary obligations so that the operation can be carried out The commitments are: The Supplier: What can be promised as an outcome (given the obligation of the customer) 6 Exercise Write a precondition for sqrt Write a postcondition for sqrt Hint: A pre/postcondition can be simulated using if statements arg >= 0 From the supplier’s side: |result2-arg| < ε The supplier’s part is a postconodition 5 From the client’s side: The client’s part is a precondition Pre- and Post-conditions Precondition: The client’s side of the contract Postcondition: The supplier’s side of the contract Because the client’s part is a condition on the arguments to the operation Because the supplier’s part is a condition on the result of the operation double sqrt(double arg) throws OutOfBounds, CalcError { if (arg < 0) // Simulate precondition throw OutOfBounds; double result; // ... Calculate square root of arg if (result*result–arg > 0.001 || // Simulate postcondition result*result–arg < -0.001) throw CalcError; return result; } 7 8 2 Using assertions in Java Assertions in Java SQRT example: double sqrt(double arg) { assert arg <= 0 : "arg is negative"; // Simulate precondition double result; // ... Calculate square root of arg assert result*result–arg < 0.001 || // Simulate postcondition result*result–arg > -0.001; return result; } By default: Assertions are disabled in run time To enable: Use run-time switches %java -enableassertions CalculateFactorial %java –enableassertions:pkg CalculateFactorial Compiling with assertions %javac -source 1.4 CalculateFactorial 9 10 Reminder: Stack Example: Stack pre- and postconditions stack.pop() Data structure: LIFO (Last In First Out) public class Stack { public void push(Object x); // Add x to top public Object pop(); // Remove and return top public Object top(); // Return top public boolean empty(); // Return true iff empty public int size(); // Return size } Precondition: not empty() Postcondition: (old size) = (size + 1) stack.push(Object x) Precondition: x != NULL Postconditions: push(3); push(2); 3 Empty==true 11 Empty==false not empty old size = size – 1 top() == x pop(); 2 3 3 s.pop() == 2 12 3 Examples (Cont.) Calculate n! (factorial n), defined: factorial(n) == 1*2*3*…*n Factorial (Cont.) Implementation #1: the recursive version Precondition: n >= 1 public int factorial(int n) { if (n == 1) return 1; return n * factorial(n-1); } Consider this implementation: public int factorial(int n) throws OutOfBounds { if (n <= 0) throw OutOfBounds; // A precondition? if (n == 1) return 1; return n * factorial(n-1); } How can we write a precondition in Java? 13 What is wrong with this solution? The condition will be tested n times, but it should only be tested once! 14 Factorial (Cont.) Factorial (Cont.) Alternative #1: Use a while loop public int factorial(int n) throws OutOfBounds { if (n < 1) throw OutOfBounds; // A precondition? i = 0; result = 1; while (i <= n) { i = i + 1; result = result * i; } } 15 Alternative #2: Use a nested method public int factorial(int n) throws OutOfBounds { if (n < 1) throw OutOfBounds; // A precondition (?) return factorial_untested(n); } private int factorial_untested(int n) { // Not a public method: no need for precondition if (n == 1) return 1; return n * factorial_untested(n-1); } 16 4 Factorial (Cont.) Assertions Alternative #3: Use Eiffel “Sanity checks”: Test that “all is well” factorial(integer: i): integer require -- Precondition not i <= 0; is // { In Java if i = 1 Result := 1 else Result := n*factorial(n-1); ensure -- Postcondition Result*Result <= 0.001 and Result*Result >= -0.001; end; -- Factorial public int factorial(int n) { if (n == 1) return 1; int fac = factorial(n-1); assert fac > n-1 || n == 2; return n * fac; } // Sanity check ... 17 18 Invariants Invariant: A condition that must always be met In code: A condition over the integrity of the program at a certain point in the control flow » Design By Contract in Eiffel deferred class TREE[G] -- ... is_leaf: boolean; arity: integer; int k; // ... assert k != 0; int l = i/k; require -- Precondition not is_root is_leaf(t) if-and-only-if arity=0 » Size() >= 0 Empty() == (Size() == 0) ensure -- Postcondition is_sibling (Result); (Result/=Void) implies Result.right_sibling=Current); Class stack: » 19 deferred -- an abstract routine Class tree: » -- Number of children left_sibling: like parent is –- Return left neighbor (if any) Class Invariant: is a condition that holds at exit of every public method. -- abstract class end; -- left_sibling -- ... invariant is_leaf = (arity = 0); child_islast = (not is_leaf and child_index = arity); end -- class TREE 20 5 Would these work in Java? public class TREE public TREE left_sibling() { TREE Result; ... // Calculate Result assert if (Result != null) // Postcondition Result.right_sibling() == this; public TREE right_sibling() { TREE Result; ... // Calculate Result assert if (Result != null) // Postcondition Result.left_sibling() == this; } Invariants in Java Invariants are not directly supported Instead: Use assertions at end of public methods public class Stack { public Stack() { assert CheckInvariants(); } public void push(int arg) { ... // Push implementation assert CheckInvariants(); } ... private boolean CheckInvariants() { return Empty() == (Size() == 0); } } public abstract class Collection abstract int size() { assert (Result >= 0) // Postcondition } 22 Invariants in Eiffel Exercise deferred class Stack -- Abstract class Write class invariants for class date Size(): int is ... -- method definition here Empty(): boolean is ... --method definition here -- remaining class definition here invariant Empty() == (Size() == 0); end -- class Stack 23 public class Date private int day month year; ... public bool is_later_than(Date other)... public bool is_earlier_than(Date other)... public add(int no_days) { // calculate new date // precondition? // postcondition? } 24 6 Exercise Offer preconditions for PhoneCall.length() Offer an invariant for class PhoneCall public class PhoneCall{ private TimeAndDate start, // defined when connection established end; // defined when connection terminates private boolean connectionEstablished; private boolean active; // True if call started but not terminated yet public TimeInterval length() { return end – start; } ... } 25 7