Procedural Abstraction and Design by Contract Paul Ammann Information & Software Engineering SWE 619 Software Construction cs.gmu.edu/~pammann/ SWE 619 © Paul Ammann Benefits of Abstraction Locality – The implementation of an abstraction can be read or written without needing to examine the implementations of any other abstractions Modifiability – An abstraction can be reimplemented without requiring changes to any abstractions that use it SWE 619 © Paul Ammann 2 Abstraction by Specification Abstraction Implementation 1 SWE 619 … © Paul Ammann Implementation N 3 Specifications Way to Define Abstractions Formality – As much as is useful – More than just English – Typically less than a language with a formal semantics – We will be close to industry standard (ie Javadoc) – Not intended to be a programming language! SWE 619 © Paul Ammann 4 Parts of a Procedural Specification Precondition: Anything missing? What happens in other languages? (eg C++) Header public static int sortedSearch (int[]a, int x) // Requires: a is sorted in ascending order // Effects: if x is in a returns an index where // x is stored; otherwise, returns -1 Postcondition (examples?) SWE 619 © Paul Ammann 5 Another Example No Precondition! (Precondition equals “true”) public static int search (int[]a, int x) // Effects: if x is in a returns an index where // x is stored; otherwise, returns -1 Underdetermined – what happens if x is in a more than once? SWE 619 © Paul Ammann 6 Yet Another Example Still no precondition (what if a is null?) Notice the “Modifies” clause Notice implicit treatment of duplicates public static void sort (int[] a) // Modifies: a // Effects: rearranges the elements of a into // ascending order // E.g. if a = [3,1,6,1] before the call, then // a = [1,1,3,6] after the call Alternate notation: // E.g. if a = [3,1,6,1], then a_post = [1,1,3,6] SWE 619 © Paul Ammann 7 Yet Another Example Procedures may modify variables that are not explicit arguments public static void copyLine() // Requires: System.in contains a line of text // Modifies: System.in and System.out // Effects: Reads a line of text from System.in, // advances the cursor in System.in to the end of // the line, and writes the line on System.out SWE 619 © Paul Ammann 8 Specifications and Implementations Minimality – minimal means fewer constraints on behavior (what does this say about the postcondition?) Underdetermined behavior – More than one possible result per input allowed Deterministic implementation – Only one result per input produced Generality – More general if specification can handle a large class of inputs. (What does this say about the precondition?) SWE 619 © Paul Ammann 9 Total vs. Partial Procedures A procedure is total if its behavior is specified for all possible inputs. A partial procedure always has a precondition. Partial procedures are less safe than total procedures. Exception handling can be used to eliminate preconditions (more in next lecture…). SWE 619 © Paul Ammann 10 Bertrand Meyer’s View: Design by Contract A programmer’s job is to produce solutions, not programs. Software should be reliable. Type safety, garbage collection, etc… – These are good – But they are not enough Correctness is a relative notion – A program is correct relative to a specification SWE 619 © Paul Ammann 11 What does a Contract Look Like? Consider the triple: {P} S {Q} – P is the precondition (Requires clause) – Q is the postcondition (Effects clause) – S is the program text The Customer (client) is obligated to establish P. The Implementor (service) may assume P The Customer is entitled to Q The Implementor is obligated to provide Q That’s it! SWE 619 © Paul Ammann 12 What happens when a Contract Breaks? If everyone does their job, there is no problem! If the precondition is not satisfied, the Customer is wrong! (The client has a bug). If the precondition is satisfied, but the postcondition is not, then the Service is wrong (The service has a bug). The Client can’t do the Service’s job! The Service can’t do the Client’s job! SWE 619 © Paul Ammann 13 Application of Contract Model to Debugging Suppose you are fixing a fault in a program. – What justification is there for a proposed change? Example Context: code considered correct ..... {P} code identifed as wrong vs. proposed correct code ..... {Q} more code considered correct SWE 619 © Paul Ammann 14 Example //Effects: if arr is null throw NPE else return the number of occurrences of 0 in arr public static in numZero (int[] arr) { int count = 0; {inv: count has # zeros in arr[0..-1]} for (int i=1; i < arr.length; i++) { Note the bug! {inv: count has # zeros in arr[0..i-1]} if (arr[i] == 0) {count++} } return count; } SWE 619 © Paul Ammann 15 What does the Client like? Since preconditions are Client obligations, the Client would prefer not to have any! From the Client’s perspective, “true” is the best precondition. In general, weaker preconditions are better. The Client is happy to have any postcondition that is strong enough to meet the Client’s needs. SWE 619 © Paul Ammann 16 What does the Server like? Since preconditions are Server benefits, the Server would prefer to have lots of them! From the Server’s perspective, “false” is the best precondition. In general, stronger preconditions mean an easier job with the implementation. The Server prefers weak postconditions. Each additional constraint in a postcondition is an additional obligation on the Server. SWE 619 © Paul Ammann 17 Who should get preference? In Business, the Customer is thought to be “always right” This is a good model for software as well: Tradeoffs should generally be made in favor of the client. That is – minimize preconditions – provide usefully strong postconditions But SWE 619 there are limits… © Paul Ammann 18 Problems with Eliminating Preconditions Forcing the Server to handle “weird” cases can lead to inefficient, bulky (read “error prone”) code. For “local” use, therefore, preconditions can be extremely powerful (and appropriate). Example: Consider the “partition” method in a quicksort routine (Liskov p. 49). It would be weird to handle the case where the array indices were out of bounds. SWE 619 © Paul Ammann 19 More Problems with Eliminating Preconditions What if the Implementer can’t provide a good “default” (ie Defensive Programming)? Consider the following (horrible) code: public double sqrt (double x) { if (x < 0) { handle problem somehow return some value (what?) } else { proceed with normal square root computation return y such that y*y is approximately x } } SWE 619 © Paul Ammann 20 Dissecting the Square Root Example What could possibly be a correct “default”? – Printing an error message? • Not a comforting thought to certain end users (ie pilots). What could possibly be a reasonable return value? The Lesson: The Server is not in a position to define behavior. That’s the Client’s job. (through the contract mechanism). SWE 619 © Paul Ammann 21 Meyer’s Perspective on Defensive Programming Defensive programming: – leads to duplicate checks on preconditions and therefore code bloat. – leads to implementers checking preconditions when they have no idea what to do if the precondition is false. – leads to confusion over responsibility for ensuring certain constraints. So, Meyer’s advice is, “Don’t do it!” – Your mileage may vary – Think about this in the context of preconditions and exception handling. What SWE 619 are the implications for security? © Paul Ammann 22