EECE 310: Software Engineering

Slides:



Advertisements
Similar presentations
Identity and Equality Based on material by Michael Ernst, University of Washington.
Advertisements

Composition CMSC 202. Code Reuse Effective software development relies on reusing existing code. Code reuse must be more than just copying code and changing.
David Evans CS201J: Engineering Software University of Virginia Computer Science Lecture 6: Reasoning about Data Abstractions.
Data Abstraction II SWE 619 Software Construction Last Modified, Spring 2009 Paul Ammann.
CSE 331 SOFTWARE DESIGN & IMPLEMENTATION ABSTRACT DATA TYPES II Autumn 2011.
CSE 331 Software Design & Implementation Dan Grossman Fall 2014 Data Abstraction: Abstract Data Types (ADTs) (Based on slides by Mike Ernst, David Notkin,
REFACTORING Lecture 4. Definition Refactoring is a process of changing the internal structure of the program, not affecting its external behavior and.
Chapter 3 Introduction to Collections – Stacks Modified
Cs2220: Engineering Software Class 8: Implementing Data Abstractions Fall 2010 University of Virginia David Evans.
1 Abstraction  Identify important aspects and ignore the details  Permeates software development programming languages are abstractions built on hardware.
Cs205: engineering software university of virginia fall 2006 Data Abstraction David Evans
EECE 310: Software Engineering Iteration Abstraction.
Copyright © 2002, Systems and Computer Engineering, Carleton University a-JavaReview.ppt * Object-Oriented Software Development Unit.
Design Patterns Gang Qian Department of Computer Science University of Central Oklahoma.
Comp 302: Software Engineering Data Abstractions.
13-1 Sets, Bags, and Tables Exam 1 due Friday, March 16 Wellesley College CS230 Lecture 13 Thursday, March 15 Handout #23.
Topic 1 Object Oriented Programming. 1-2 Objectives To review the concepts and terminology of object-oriented programming To discuss some features of.
Data Abstractions EECE 310: Software Engineering.
CSE 331 Software Design & Implementation Hal Perkins Autumn 2012 Abstract Data Types – Examples / Summary (Based on slides by Mike Ernst and David Notkin)
Type Abstraction SWE Spring October 05Kaushik, Ammann Substitution Principle “In any client code, if supertype object is substituted.
Data Abstraction Gang Qian Department of Computer Science University of Central Oklahoma.
ANU COMP2110 Software Design in 2003 Lecture 10Slide 1 COMP2110 Software Design in 2004 Lecture 12 Documenting Detailed Design How to write down detailed.
Data Abstraction SWE 619 Software Construction Last Modified, Spring 2009 Paul Ammann.
Programming Techniques Lec03 Procedural Abstraction Software Engineering Fall 2005.
Understanding ADTs CSE 331 University of Washington.
Representation invariants and abstraction functions CSE 331 UW CSE.
PROGRAMMING PRE- AND POSTCONDITIONS, INVARIANTS AND METHOD CONTRACTS B MODULE 2: SOFTWARE SYSTEMS 13 NOVEMBER 2013.
CSE 331 Software Design & Implementation Dan Grossman Fall 2014 Abstraction Functions (Based on slides by Mike Ernst, David Notkin, Hal Perkins)
Iteration Abstraction SWE Software Construction Fall 2009.
Abstraction, Specification and Verification. Comp 302, Spring Abstraction Hiding or overlooking detail that is not relevant for the task at hand.
CSE 331 SOFTWARE DESIGN & IMPLEMENTATION ABSTRACT DATA TYPES Autumn 2011.
Type Hierarchies. Type Hieararchy Why?: Want to define family of related types. At the top of the hierarchy, a type whose spec defines behavior common.
David Evans CS201J: Engineering Software University of Virginia Computer Science Lecture 7: A Tale of Two Graphs (and.
David Evans CS201J: Engineering Software University of Virginia Computer Science Lecture 5: Implementing Data Abstractions.
Zach Tatlock / Winter 2016 CSE 331 Software Design and Implementation Lecture 6 Representation Invariants.
Reasoning and Design (and Assertions). How to Design Your Code The hard way: Just start coding. When something doesn’t work, code some more! The easier.
Modular Decomposition, Abstraction and Specifications
EECE 310: Software Engineering
EECE 310: Software Engineering
Java Programming: Guided Learning with Early Objects
CSE 331 Software Design & Implementation
CSE 331 Software Design & Implementation
CSE 331 Software Design and Implementation
Type Abstraction SWE Spring 2009.
Classes and Objects 2nd Lecture
Lecture 9 Concepts of Programming Languages
CSE 331 Software Design and Implementation
CSE 331 Software Design and Implementation
CSE 331 Software Design & Implementation
Type Abstraction Liskov, Chapter 7.
Data Abstraction David Evans cs205: engineering software
Iteration Abstraction
SWE 619 Software Construction Last Modified, Fall 2015 Paul Ammann
Method Verification CS/SWE 332 Paul Ammann.
Lecture 7: A Tale of Two Graphs CS201j: Engineering Software
Lecture 4: Data Abstraction CS201j: Engineering Software
Protocols CS 4311 Wirfs Brock et al., Designing Object-Oriented Software, Prentice Hall, (Chapter 8) Meyer, B., Applying design by contract, Computer,
CSE 331 Software Design and Implementation
Introduction to Data Structure
CSE 331 Software Design & Implementation
CSE 331 Software Design & Implementation
CS 112 Programming 2 Lecture 02 Abstract Classes & Interfaces (2)
Classes and Objects Reusing Classes with Composition
Type Abstraction SWE Spring 2013.
CMPE212 – Reminders Assignment 2 due next Friday.
Software Specifications
Reasoning about Data Abstractions
C++ Object Oriented 1.
Lecture 9 Concepts of Programming Languages
Method Verification Paul Ammann.
Presentation transcript:

EECE 310: Software Engineering Data Abstractions EECE 310: Software Engineering

What will we learn ? Data abstractions Implementing data abstractions What are they and how to specify them ? How to use them Implementing data abstractions Rep Invariant and Abstraction Functions Reasoning about the correctness of data abstractions and their implementations Equality testing and mutability

Data Abstraction Introduction of a new type in the language Type can be abstract or concrete Has one of more constructors and operations Type can be used like a language type Both the code and the data associated with the type is encapsulated in the type specification No need to expose the representation to clients Clients do not depend on implementation Advantages: locality & modifiability

Why do we need new types ? Imagine we want to hold information about dates E.g. year, month, day, hours, minutes, seconds, day of week. Could use integer arrays, see right But suppose we then decide years need 4 digits rather than 2 (i.e. ,2001 instead of 01) We have to change every part of the program that uses dates e.g. if we used arrays of byte for date & time: byte today[3]; byte lecture1_time[3]; … today[0] = 10; today[1] = 10; today[2] = 11; lecture1_time[0] = 17; lecture1_time[1] = 0; lecture1_time[2] = 0;

What does data abstraction provide? Extending programming language Locality & Modifiability Deferring decision on data representation Changing implementation details

Isn’t this OOP ? NO, though OOP is one way to implement it OOP is a way of organizing programs into classes and objects. Data abstraction is a way of introducing new types with meanings. Encapsulation is a goal shared by both. But data abstraction is more than creating classes. In Java, every data abstraction can be implemented by a class declaration. But every class declaration is not a data abstraction.

Difference between Data Abstraction and OOP A data-structure representing a set of Integers may be a data abstraction (IntSet) Data type has a well-defined meaning Can be used in variable declarations An object may hold many unrelated data-structures in a program (IntSet and List) Convenient for code organization and reuse But does not necessarily have a clear meaning

Example: IntSet Consider a IntSet Data type that we wish to introduce in the language. What should it have Constructors to create the data-type from scratch or from other data types (e.g., lists) Operations: insert, remove, size and isIn A specification of what the data type represents Internal representation to implement the type

Data Type Specification IntSet Example public class IntSet { // An IntSet is a mutable, bounded set of Integers. // A typical IntSet is {x1, x2, …. , xn } // constructors public IntSet(); // Mutators public void insert( int x ); public void remove( int x ); // Observers public int size(); } Class declaration Overview of data type Constructors Operations (only public ones) * Mutators Observers Producers

Data Type Overview Specification at the top of the class declaration Explains what is the data-type being modeled Whether instances are mutable (can be changed) Whether it is bounded What is a typical example of the data-type Example: // An IntSet is a mutable, bounded set of Integers. // A typical IntSet is written as {x1, x2, …. , xn }

Constructors Initializes the data-type instance (this) public IntSet( ) { // EFFECTS: Initializes this to be the empty set Initialization can be from one of the following Nothing Primitive types (e.g., singleton set) Other objects (e.g., another IntSet)

Operations Mutators Observers Modify the state of the object instance Cannot be specified for immutable types Example: public void remove(int x) { // MODIFIES: this // EFFECTS: Removes x from // this, this_post = this – { x } Do not modify the state of the object Can be specified for both mutable & immutable types Example: public int size() { // MODIFIES: None // EFFECTS: Returns the // cardinality of this

Putting it together: IntSet Specification public class IntSet { //OVERVIEW: IntSets are mutable, unbounded sets of integers. // A typical IntSet is {x1, …xn}, where xi are all integeres // Constructors public IntSet(); //EFFECTS: Initializes this to be the empty set // Mutators public void insert (int x); // MODIFIES: this // EFFECTS: adds x to the set this, i.e, this_post = this u {x} public void remove (int x); // EFFECTS: this_post = this - {x} //Observers public boolean isIn(int x); // EFFECTS: returns true if x e this, false otherwise public int size(); // EFFECTS: Returns the cardinality of this }

Using ADTs: Rules Must be based on the specification of the data abstraction and not its implementation Implementation can change without notice Can perform the following operations: Use constructors to create new objects Can access object’s state through public methods Can change object’s state only if it is mutable

Using ADTs: Example public static IntSet populateElements (int [ ] a) throws NullPointerException { // EFFECTS: If a is null throws NullPointerException, // else returns a set containing an entry // for each distinct element of a. IntSet s = new IntSet( ); for (int i =0; i < a.length; ++i) { s.insert(a[i]); } return s; }

W7

[last time] Data Abstraction Specifying Abstract Data Types Implementing Abstract Data Types

A big program can have thousands of procedures Managing Complexity Procedural Abstraction Divide problem into procedures Use specifications to separate what from how A big program can have thousands of procedures

Data Abstraction Organize program around abstract data types (ADT) Group procedures by the data they manipulate Hide how data is represented from how it is used

Abstract Data Types Separate what you (can) do with data from how it is represented Client interacts with data through provided operations according to their specifications Implementation chooses how to represent data and implement its operations What should the specification of a datatype look like?

Specifying Abstract Data Types Overview: what does the type represent Mutability/Immutability e.g., A String is an immutable sequence of characters. Introduce Abstract Notation e.g., A typical Set is { x1, …, xn }. Operations: specifications for constructors and methods clients use Describe in terms of abstract notation introduced in overview.

Example: StringStack public class StringStack Note: Java provides java.util.Stack, but we’ll implement our own Stack datatype. public class StringStack OVERVIEW: A StringStack represents a mutable last-in-first-out stack where all elements are Strings. A typical stack is [ e_n-1, e_n-2, ..., e_1, e_0 ] where e_n-1 is the top of the stack.

public class StringStack OVERVIEW: A StringStack represents a mutable last-in-first-out stack where all elements are Strings. A typical stack is [ e_n-1, e_n-2, ..., e_1, e_0 ] where e_n-1 is the top of stack. public StringStack() EFFECTS: Initializes this as an empty stack. public void push(String s) MODIFIES: this EFFECTS: Pushes s on the top of this. For example, if this_pre = [ e_n-1, e_n-2, ..., e_1, e_0 ], this_post = [ s, e_n-1, e_n-2, ..., e_1, e_0 ] public String pop() throws EmptyStackException EFFECTS: If this is empty, throws EmptyStackException. Otherwise, returns the element on top of this and removes that element from this. this_post = [ e_n-2, ..., e_1, e_0 ] and the result is e_n-1. public String toString() EFFECTS: Returns a string representation of this.

Components of Data Abstractions Ways to create new objects of the type Constructors: create new objects of the ADT from parameters of other types Producers: create new objects of the ADT from parameters of the ADT type (and other types) Ways to observe properties: observers Ways to change properties: mutators Which of these must all (useful) types have?

public class StringStack OVERVIEW: A StringStack represents a mutable last-in-first-out stack where all elements are Strings. A typical stack is [ e_n-1, e_n-2, ..., e_1, e_0 ] where e_n-1 is the top of the stack. public StringStack() EFFECTS: Initializes this as an empty stack. public void push(String s) MODIFIES: this EFFECTS: Pushes s on the top of this. For example, if this_pre = [ e_n-1, e_n-2, ..., e_1, e_0 ], this_post = [ s, e_n-1, e_n-2, ..., e_1, e_0 ] public String pop() throws EmptyStackException EFFECTS: If this is empty, throws EmptyStackException. Otherwise, returns the element on top of this and removes that element from this. this_post = [ e_n-2, ..., e_1, e_0 ] and the result is e_n-1. public String toString() EFFECTS: Returns a string representation of this. Constructor Mutator Observer and Mutator Observer A producer?

What will we learn ? Data abstractions Implementing data abstractions What are they and how to specify them ? How to use them Implementing data abstractions Rep Invariant and Abstraction Functions Proving correctness of data abstractions Equality testing and mutability

Abstraction vs. Representation Mapping between abstract objects and their representation Every abstract object must have a representation Several representations might map to the same abstraction { 1, 2, 3 } 1 2 3 rep objects abstract objects Abstraction Representation

IntSet Specification public class IntSet { //OVERVIEW: IntSets are mutable, unbounded sets of integers. // A typical IntSet is {x1, …xn}, where xi are all int // Constructors public IntSet(); //EFFECTS: Initializes this to be the empty set // Mutators public void insert (int x); // MODIFIES: this // EFFECTS: adds x to the set this, // i.e, this_post = this u {x} public void remove (int x); // EFFECTS: this_post = this - {x} //Observers public boolean IsIn(int x); // EFFECTS: returns true if x e this, false otherwise public int size(); // EFFECTS: Returns the cardinality of this }

IntSet: Implementation Strategies Unsorted vector Rep: Vector of Integers, for each element of set called elems Insert, delete and membership require a linear scan of the array and can take O(N) time Size() takes O(1) time Bitmap Rep: Vector of Boolean, for each possible Integer value, called elems Insert, delete and membership take O(1) time on average, i.e., simple array indexing Size() can take O(N) time Tradeoffs: Does representation allow duplicates? Sort the vector?

Representation <-> Abstraction N Vector ‘elems’ of size N Vector directly holds the set elements if integer e is in the set, there exists 0 <= i < N, such that elems[i] = e Similarly, if an integer e is in the vector, then e belongs to the set Vector is a bitmap for denoting set elements If integer i is in the set, then elems[i] = True, else elems[i] = False Cannot represent integers outside range [0, N - 1]

Abstraction Function Mapping between abstraction (abstract state) and the representation (concrete state) Captures designer’s intent in choosing the rep Describes what instance variables to use How do the variables relate to the abstract object that they are intended to represent ? Makes this mapping explicit as code comments

IntSet: Abstraction Function Unsorted Array Boolean Vector AF ( c ) = { c.elems[i].intValue 0 <= i < c.elems.size } AF( c ) = { j | 0 <= j < 100 && c.elems[j] } Can be implemented by the member function: toString public void toString() { // EFFECTS: Converts the representation to a string // that represents the abstraction e.g., {x1, x2… xn} }

Implementation public class IntSet { // OVERVIEW: IntSets are mutable, unbounded sets of integers. // A typical IntSet is {x1, …xn}, where xi are all integers Vector<Integer> elems; // Rep // AF(c) = { c.elems[i].intValue // 0 <= i < c.elems.size // } public class IntSet { // OVERVIEW: IntSets are mutable, unbounded sets of integers. // A typical IntSet is {x1, …xn}, where xi are all integers Vector<Boolean> elems; // Rep // AF( c ) = { // j | 0 <= j < 100 && // c.elems[j].booleanValue // }

Abstraction Function: Points to Note The abstraction function is defined for concrete instances of the class ‘c’, and only includes the instance variables of the class The abstraction function implicitly assumes that the representation is valid What happens if the vector contains duplicate entries in the first scenario ? What happens if the bitmap contains values other than 0 or 1 ?

Representation Invariant Captures formally the assumptions on which the abstraction function is based Defines whether a particular representation is legal – invariant holds only for legal reps. Q: Should representation satisfy the representation invariant at all times Yes: except when executing the ADT’s methods

IntSet: Representation Invariant Unsorted Arrays Boolean Vector c.elems != null && (there are no duplicates in c.elems i.e., for 0<=i, j <N,) c.elems[i].intValue = c.elems[j].intValue=> i = j. c.elements != null && c.elements.size = 100 Can be implemented by the member function: repOK public void repOK() { // EFFECTS: Return true if the repInvariant is //satisfied, false otherwise }

Implementation public class IntSet { //OVERVIEW: IntSets are mutable, unbounded sets of integers. // A typical IntSet is {x1, …xn}, where xi are all integers Vector<Integer> elems; // Rep // AF(c) = { c.elems[i].intValue // 0 <= i < c.elems.size // } // RI = c.elems != NULL && // c.elems has no duplicates public class IntSet { //OVERVIEW: IntSets are mutable, unbounded sets of integers. // A typical IntSet is {x1, …xn}, where xi are all integers Vector<Boolean> elems // Rep // AF( c ) = { // j | 0 <= j < 100 && // c.elems[j].booleanValue // } // RI = c.elems != NULL && // c.elems.size = 100 // ???

Rep Invariant: Important Points Rep invariant always holds before and after the execution of the ADT’s operations Can be violated while executing the ADT’s operations When should you define rep invariant? Before any operation of ADT is implemented Right after you write the Abstraction Function (AF) How much shall the rep invariant constrain? Just enough for different developers to implement different operations AND not talk to each other No need to repeat what is in the type signature

Implementation RepOK function toString function Public method to check if the rep invariant holds Useful for testing/debugging public boolean repOK() { // EFFECTS: Returns true // if the rep invariant holds, // Returns false otherwise } Public method to convert a valid rep to a String form Useful for debugging/printing public String toString( ) { // EFFECTS: Returns a string // containing the abstraction // represented by the rep.

One more quiz-like question: For the class specified below chose the representation, write the representation function, the representation invariant; write the toString and repOk methods public class StringStack OVERVIEW: A StringStack represents a mutable last-in-first-out stack where all elements are Strings. A typical stack is [ e_n-1, e_n-2, ..., e_1, e_0 ] where e_n-1 is the top of stack. public StringStack() EFFECTS: Initializes this as an empty stack. public void push(String s) MODIFIES: this EFFECTS: Pushes s on the top of this. For example, if this_pre = [ e_n-1, e_n-2, ..., e_1, e_0 ], this_post = [ s, e_n-1, e_n-2, ..., e_1, e_0 ] public String pop() throws EmptyStackException EFFECTS: If this is empty, throws EmptyStackException. Otherwise, returns the element on top of this and removes that element from this. this_post = [ e_n-2, ..., e_1, e_0 ] and the result is e_n-1.

One more Consider a Polynomial data type represented as an array. The co-efficients of the term xi are stored in the ith element of trms array. Write its abstraction function Write its rep-invariant Write the repOK and toString functions. How would your answer change if the polynomial was represented as a list ?

Menu Data abstractions Implementing data abstractions What are they and how to specify them ? How to use them Implementing data abstractions Rep Invariant and Abstraction Functions Exposing the rep Equality testing and mutability Proving correctness of data abstractions

Is it a good idea to make the representation available so that it can be modified (outside of using clas’ instance methods)?

Mistakes that lead to exposing the rep - 1 Making rep components public public class IntSet { public Vector<Integer> elements; Your rep must always be private. Otherwise, all bets are off. Hopefully, your code will not have this bug ….

Mistakes that lead to exposing the rep - 2 public class IntSet { //OVERVIEW: IntSets are mutable, unbounded sets of integers. // A typical IntSet is {x1, …xn} private Vector<Integer> elems; // no duplicates in vector public Vector<Integer> allElements (){ //EFFECTS: Returns a vector containing the elements of this, // each exactly once, in arbitrary order return elems; } }; intSet = new IntSet(); intSet.allElements().add( new Integer(5) ); intSet.allElements().add( new Integer(5) ); // RI violated – duplicates !

Mistakes that lead to exposing the rep - 3 public class IntSet { //OVERVIEW: IntSets are mutable, unbounded sets of integers. // A typical IntSet is {x1, …xn} private Vector<Integer> elems; //constructors public IntSet (Vector<Integer> els) throws NullPointerException { //EFFECTS: If els is null, throws NullPointerException, else // initializes this to contain as elements all the ints in els. if (els == null) throw new NullPointerException(); elems = els; } }; Vector<Integer> someVector = new Vector(); intSet = new IntSet(someVector); someVector.add( new Integer(5) ); someVector.add( new Integer(5) ); // RI violated – duplicates !

Summary of mistakes that expose the Rep NOT making rep components private Returning a reference to the rep’s mutable components Initializing rep components with a reference to an “outside” mutable object NOT performing deep copy of rep elements Use clone method instead Perform manual copies

What will we learn ? Data abstractions Implementing data abstractions What are they and how to specify them ? How to use them Implementing data abstractions Rep Invariant and Abstraction Functions Proving correctness of data abstractions Equality testing and mutability

Mutable objects Objects whose abstract state can be modified Applies to the abstraction, not the representation Mutable objects: Can be modified once they are created e.g., IntSet, IntList etc. Immutable objects: Cannot be modified Examples: Polynomials, Strings

Equality: Equals Method All objects are inherited from object which has a method “Boolean equals(Object o)” Returns true if object o is the same as the current Returns false otherwise Note that equals tests whether two objects have the same state If a and b are different objects, a.equals(b) will return false even if they are functionally identical

Equality: IntSet Example IntSet a = new IntSet(); a.insert(1); a.insert(2); a.insert(3); IntSet b = new IntSet(); b.insert(1); b.insert(2); b.insert(3); if ( a.equals(b) ) { System.out.println(“Equal”); } What is printed by the above code ?

Equality: IntSet Example It prints nothing. Why ? Because the intsets are different objects and the object.equals method only compares their hash Therefore, a.equals(b) returns false But this is in fact the correct behavior ! To see this, assume that you added an element to a but not b after the equals comparison a.equals(b) would no longer be true, even if you have not changed the references to a or b

Rule of Object Equality Two objects should be equal if it is impossible to distinguish between them using any sequence of calls to the object’s methods Corollary: Once two objects are equal, they should always be equal. Otherwise it is possible to distinguish between them using some combination of the object’s methods.

IntSet Example: Revisited In the IntSet example, you could distinguish between two IntSets by adding different elements to each set after the comparison. Therefore, they are NOT equal. However, if the objects are immutable AND have the same state, then the equality check should return true, i.e., in Equals method

Immutable Abstractions ADT does not change once created No mutator methods Producer methods to create new objects Appropriate for modeling objects that do not change during their existence Mathematical entities such as Rational numbers Certain objects may be implemented more efficiently e.g., Strings

Why use immutable ADTs ? Safety Efficiency Ease of Implementation Don’t need to worry about accidental changes Can be assured that rep doesn’t change Efficiency May hurt efficiency if you need to copy the object In some cases, it may be more efficient by sharing representations across objects e.g., Strings Ease of Implementation May be easier for concurrency control

Immutable ADT: Example - 1 public class Rational { // A typical rational is n/d private int num; private int denom; // AF ( c ) = c.num / c.denom // Rep Inv: c.denom > 0 Rational(int n, int d) throws ZeroException { // EFFECTS: … if (d == 0) throw new ZeroException; num = n; denom = d; } …

Immutable ADT: Example - 2 // Does this change the abstraction ? private void reduce( ) { // REQUIRES: this.num =/= 0 // MODIFIES: this // EFFECTS: Changes this to reduced form int temp = num; if (num < 0) temp = -num; int g = Num.gcd( temp, denom ); num = num / g; denom = denom / g; }

Immutable ADT: Example - 3 // Check if two Rationals are equal in reduced forms public void equals(Rational r) { if (r == null) return false; if (num ==0 || r.num == 0) return (num == r.num); reduce(); r.reduce(); return (r.num == num && r.denom == denom); }

Benevolent side effects The equals method changes the representation but not the abstraction Hence, it is said to have a “benevolent” side effect Benevolent side-effect is a modification that is not visible outside implementation of the ADT Possible only when AF is many-to-one All side-effects of immutable abstractions must be benevolent (assuming a mutable rep)

Equality: Immutable objects Immutable objects should define their own equals method Return true if the abstract state matches, even if the internal state is different Methods of an Immutable object can modify its rep, but not the abstraction Such methods said to have benevolent side effects

Summary Data abstractions Implementing data abstractions What are they and how to specify them ? How to use them Implementing data abstractions Rep Invariant and Abstraction Functions Equality testing and mutability Proving correctness of data abstractions

Reasoning about ADTs - 1 ADTs have state in the form of representation Need to consider what happens over a sequence of operations on the abstraction Correctness of one operation depends on correctness of previous operations We need to reason inductively over the operations of the ADT Show that constructor is correct Show that each operation is correct

Reasoning about ADTs - 2 First, need to show that the rep invariant is maintained by the constructor & operations Then, show that the implementation of the abstraction matches the specification Assume that the rep invariant is maintained Use the abstraction function to map the representation to the abstraction

Why show that Rep Invariant is maintained ? Consider the implementation of the IntSet using the unsorted vector representation. We wish to compute the size of the set (i.e., its cardinality). public int size() { return elems.size(); } Is the above implementation correct ?

Why show that Rep Invariant is maintained ? Yes, but only if the Rep Invariant holds ! c.elems != Null && c.elems has no duplicates Otherwise, size can return a value >= cardinality public int size() { return elems.size(); }

Showing Rep Invariant is maintained: Data Type Induction Show that the constructor establishes the Rep Invariant For all other operations, Assume at the time of the call the invariant holds for this and all argument objects of the type Demonstrate that the invariant holds on return for this for returned objects of the type A Valid Rep Function Body Another Valid Rep

IntSet : getIndex Assume that IntSet has the following private function. private int getIndex( int x ) { // EFFECTS: If x is in this, returns index // where x appears in the Vector elems // else return -1 (do NOT throw an exception) for (int i = 0; i < els.size( ); i ++ ) if ( x == elements.get(i).intValue() ) return i; return –1; }

IntSet: Constructor RI: c.elems != NULL && c.elems has no duplicates Show that the RI is true at the end of the constructor public IntSet( ) { // EFFECTS: Initializes this to be empty elems = new Vector<Integer>(); } RI: c.elems != NULL && c.elems has no duplicates Proof: Clause 1 is satisfied because the elems vector is initialized by constructor Clause 2 is satisfied because elems has no elements (and hence no duplicates)

IntSet: Insert RI: c.elems != NULL && c.elems has no duplicates Show that if RI holds at the beginning, it holds at the end. public void insert (int x) { // MODIFIES: this // EFFECTS: adds x to the set such that this_post = this u {x} if ( getIndex(x) < 0 ) elems.add( new Integer(x) ); } RI: c.elems != NULL && c.elems has no duplicates Proof: If clause 1 holds at the beginning, it holds at the end of the procedure. - Because c.elems is not changed by the procedure. If clause 2 holds at the beginning, it holds at the end of the procedure - Because getIndex() prevents duplicate elements from being added to the vector

IntSet:Remove RI: c.elems != NULL && c.elems has no duplicates Show that if RI holds at the beginning, it holds at the end. pubic void remove(int x) { // MODIFIES: this // EFFECTS: this_post = this - {x} int i = getIndex(x); if (i < 0) return; // Not found elems.set(i, elems.lastElement() ); elems.remove(elems.size() – 1); } RI: c.elems != NULL && c.elems has no duplicates

Other operations Show that if RI holds at the beginning, it holds at the end. public int size() { return elems.size(); } Proof: Both clauses hold because the operation does not modify the rep. public boolean isIn(int x) { return getIndex(x) > 0; } Proof: Both clauses hold because the operation does not modify the rep. RI: c.elems != NULL && c.elems has no duplicates

Rep Invariant Thus, we have shown that the RI is established by the constructor and holds for each operation (i.e., if RI is true at the beginning, it is true at the end). Can we stop here ? No. To see why not, consider an implementation of the operators that does nothing. Such an implementation will satisfy the rep invariant, but is clearly wrong !!! To complete the proof, we need to show that the Abstraction provided by the ADT is correct. For this, we use the (now proven) fact that the RI holds and use the AF to show that the rep satisfies the AF’s abstraction after each operation.

Abstraction Function: IntSet Show that the implementation matches the ADT’s specification (i.e., its abstraction) Pre-Rep Abstraction function Pre-Abstraction Function Spec Function Implementation Abstraction function Post- Rep Post-Abstraction

Abstraction Function: Constructor AF ( c ) = { c.elems[i].intValue | 0 <= i < c.elems.size } public IntSet( ) { // EFFECTS: Initializes this to be empty elems = new Vector<Integer>() ; } AF Empty vector Empty Set Proof: Constructor creates an empty set, so it is correct.

Abstraction Function: Size AF ( c ) = { c.elems[i].intValue | 0 <= i < c.elems.size } public int size() { // EFFECTS: Returns the cardinality of this return elems.size( ); } AF Number of elements in vector Cardinality of the set (Why ?) Proof: Because the rep invariant guarantees that there are no duplicates in the vector, the number of elements in the vector denotes the cardinality of the set.

Abstraction Function: Insert AF ( c ) = { c.elems[i].intValue | 0 <= i < c.elems.size } AF public void insert (int x) { // MODIFIES: this // EFFECTS: adds x to the set // such that this_post = this U {x} if ( getIndex(x) < 0 ) elems.add(new Integer(x)); } Vector this Implementation Vector with element added if and only if it did not already exist this_post = this U {x} AF

Abstraction Function: Remove AF ( c ) = { c.elems[i].intValue| 0 <= i < c.elems.size } Vector this public void remove (int x) { // MODIFIES: this // EFFECTS: this_post = this - {x} int i = getIndex(x); if (i < 0) return; // Not found // Move last element to the index i elems.set(i, elems.lastElement() ); elems.remove(elems.size() – 1); } Vector with first instance of element removed if it exists this_post = this - {x}

Abstraction Function: IsIn AF ( c ) = { c.elems[i].intValue| 0 <= i < c.elems.size } public boolean isIn(int x) { // EFFECTS: Returns true if x belongs to // this, false otherwise return getIndex(x) > 0; } vector this True if and only if x is present in the vector True if x belongs to this, False otherwise

Proof Summary This completes the proof. Thus, we’ve shown that the ADT implements it spec correcltly. This method is called “Data type induction”, because it proceeds using induction. Step 0: Write the implementation of the ADT Step 1: Show that the RI is maintained by the ADT Step 2: Assuming that the RI is maintained, show using the AF that the translation from the rep to the abstraction matches the method’s spec.

Group Exercise Write the implementation of the Polynomial data type discussed earlier. Write its RI and AF. Show using data-type induction that the ADT’s implementation matches its specification.

Summary Data abstractions Implementing data abstractions What are they and how to specify them ? How to use them Implementing data abstractions Rep Invariant and Abstraction Functions Proving correctness of data abstractions Equality testing and mutability