
Building ADTs for Reusability Abstract Data Types (ADTs), ADTs are reusable software components Classes, and Objects e.g., Stacks, Queues, Lists, Dictionaries, Trees, Graphs ● Readings: OOSC2 Chapters 6, 7, 8 An ADT, once thoroughly tested, can be reused by: Suppliers of other ADTs ● Clients of Applications As○ a supplier, you are obliged to: ○ Implement given ADTs using other ADTs (e.g., arrays, linked lists, ● hash tables, etc.) ○ EECS3311: Software Design Design algorithms that make use of standard ADTs For each ADT that you build, you ought to be clear about: Fall 2017 ○ The list of supported operations (i.e., interface ) ● The interface of an ADT should be more than method signatures and CHEN-WEI WANG ○ natural language descriptions: ● How are clients supposed to use these methods? [ preconditions ] ● What are the services provided by suppliers? [ postconditions ] Time● (and sometimes space) complexity of each operation 3 of 33 ○ 2 Abstract Data Type (ADT) “abstract” implementation details are not specified ! Abstract Data⇒ Types (ADTs) Why Java Interfaces Unacceptable ADTs (1) Abstract Data Type –entity that consists of: Given a problem, you1) are data required structure (DS) to filter out irrelevant details. 2) set of operation supported on the DS The result is an abstract data type (ADT) , whose interface ● 3) error conditions consists of a list of (unimplemented) operations. ● ADT Interface request Data add() Structure remove() result find() Supplier’s Obligations: ImplementBasic Data Structures all operations• array ● Choose(used in advanced the “right” ADT) data• structurelinked list (DS) It is useful to have: Client○ ’s Benefits: A generic collection class where the homogeneous type of ○ Correct output elements are parameterized as E. ● ● Efficient performance A reasonably intuitive overview of the ADT. The○ internal details of an implemented ADT should be hidden. ○ Java 8 List API 2 of 33 ●4 of 33 ● Why Java Interfaces Unacceptable ADTs (2) Why Eiffel Contract Views are ADTs (2) Methods described in a natural language can be ambiguous: Even better, the direct correspondence from Eiffel operators to logic allow us to present a precise behavioural view. 5 of 33 7 of 33 Why Eiffel Contract Views are ADTs (1) Uniform Access Principle (1) class interface ARRAYED_CONTAINER We may implement Point using two representation systems: feature -- Commands assign_at (i: INTEGER; s: STRING) ● -- Change the value at position ’i’ to ’s’. require valid_index: 1 <= i and i <= count ensure size_unchanged: imp.count =(old imp.twin).count item_assigned: imp [i] s The Cartesian system stores the absolute positions of x and y. others_unchanged: The Polar system stores the relative position: the angle (in radian) ∼ across ○ phi and distance r from the origin 0.0 . 1 |..| imp.count as j ○ all How the Point is implemented is irrelevant to users: j.item /= i implies imp [j.item] (old imp.twin)[j.item] Imp. 1: Store x and y. [ Compute( ) r and phi on demand ] end ● ∼ Imp. 2: Store r and phi. [ Compute x and y on demand ] count: INTEGER ○ invariant As far as users of a Point object p is concerned, having a ○ consistency: imp.count = count uniform access by always being able to call p.x and p.y is end -- class ARRAYED_CONTAINER ● what matters, despite Imp. 1 or Imp. 2 being current strategy. 6 of 33 8 of 33 Uniform Access Principle (2) Uniform Access Principle (4) Let’s say the supplier decides ( secretly ) to adopt strategy Imp. 2. class POINT class POINT -- Version 2 create feature -- Attributes make_cartisian, make_polar r : REAL feature -- Public, Uniform Access to x- and y-coordinates p : REAL x : REAL feature -- Constructors y : REAL make_polar(nr: REAL; np: REAL) end do r := nr p := np A class Point declares how users may access a point: either end get its x coordinate or its y coordinate. feature -- Queries ● We offer two possible ways to instantiating a 2-D point: x : REAL do Result := r cos p end make cartisian (nx: REAL; ny: REAL) x : REAL do Result := r sin p end end × ( ) ● make polar (nr: REAL; np: REAL) × ( ) Features○ x and y, from the client’s point of view, cannot tell Attributes r and p represent the Polar system whether○ it is implemented via: A client still accesses a point p via p.x and p.y. ● Storage [ x and y stored as real-valued attributes ] ● Extra Computations: computing x and y according to the current Computation [ x and y defined as queries returning real values ] ● values of r and p. 9 of○ 33 11 of○ 33 ○ Uniform Access Principle (3) Uniform Access Principle (5.1) Let’s say the supplier decides to adopt strategy Imp. 1. Let’s consider the following scenario as an example: class POINT -- Version 1 feature -- Attributes x : REAL y : REAL feature -- Constructors make_cartisian(nx: REAL; nx: REAL) do x := nx y := ny end end Attributes x and y represent the Cartesian system A client accesses a point p via p.x and p.y. ● No Extra Computations: just returning current values of x and y. ● However, it’s harder to implement the other constructor: the ○ body of make polar (nr: REAL; np: REAL) has to ● compute and store x and y according to the inputs nr and np. Note: 360 2⇡ 10 of 33 12 of 33 ○ = Uniform Access Principle (5.2) Generic Collection Class: Motivation (1) class STRING _STACK 1 test_points: BOOLEAN feature {NONE} -- Implementation 2 local 3 A, X, Y: REAL imp: ARRAY[ STRING ];i: INTEGER 4 p1, p2: POINT feature -- Queries 5 do count: INTEGER do Result := i end 6 comment("test: two systems of points") -- Number of items on stack. 7 A := 5; X := A 3; Y := A top: STRING do Result := imp [i] end -- Return top of stack. 8 create {POINT} p1√.make_cartisian (X, Y) create {POINT} ×p2.make_polar ( , 1 ) feature -- Commands 9 2 A 6 ⇡ 10 Result := p1.x = p2.x and p1.y = p2.y push (v: STRING ) do imp[i] := v; i := i +1end 11 end × -- Add ’v’ to top of stack. pop do i := i -1end -- Remove top of stack. If strategy Imp. 1 is adopted: end L8 is computationally cheaper than L9. [ x and y attributes ] ● L10 requires no computations to access x and y. Does how we implement integer stack operations (e.g., top, If○ strategy Imp. 2 is adopted: push, pop) depends on features specific to element type STRING ○ L9 is computationally cheaper than L8. [ r and p attributes ] ○ (e.g., at, append)? [ NO! ] L10 requires computations to access x and y. How would you implement another class ACCOUNT STACK? 13 of○ 33 15 of 33 ○ ○ Uniform Access Principle (6) Generic Collection Class: Motivation (2) The Uniform Access Principle : class ACCOUNT _STACK Allows clients to use services (e.g., p.x and p.y) regardless of feature {NONE} -- Implementation how they are implemented. imp: ARRAY[ ACCOUNT ];i: INTEGER ● feature -- Queries Gives suppliers complete freedom as to how to implement the count: INTEGER do Result := i end services (e.g., Cartesian vs. Polar). -- Number of items on stack. ● No right or wrong implementation; it depends! top: ACCOUNT do Result := imp [i] end Choose for storage if the services are frequently accessed and -- Return top of stack. ○ their computations are expensive. feature -- Commands ○ push (v: ACCOUNT ) do imp[i] := v; i := i +1end e.g. balance of a bank involves a large number of accounts -- Add ’v’ to top of stack. Implement balance as an attribute pop do i := i -1end Choose for computation if the services are not keeping their -- Remove top of stack. values⇒ in sync is complicated. end ○ e.g., update balance upon a local deposit or withdrawal Implement balance as a query Does how we implement integer stack operations (e.g., top, Whether it’s storage or computation, you can always change push, pop) depends on features specific to element type ⇒ ○ ACCOUNT deposit withdraw secretly , since the clients’ access to the services is uniform . (e.g., , )? [ NO! ] ●14 of 33 16 of 33 Generic Collection Class: Supplier Generic Collection Class: Client (1.2) Your design “smells” if you have to create an almost identical As client, declaring ss: STACK[ ACCOUNT ] instantiates every new class (hence code duplicates ) for every stack element ● occurrence of G as ACCOUNT. type you need (e.g., INTEGER, CHARACTER, PERSON, etc.). Instead, as supplier, use G to parameterize element type: class STACK [¡G ACCOUNT] feature {NONE} -- Implementation class STACK [G] ● imp: ARRAY[ ¡G ACCOUNT ];i: INTEGER feature {NONE} -- Implementation feature -- Queries imp: ARRAY[ G ];i: INTEGER count: INTEGER do Result := i end feature -- Queries -- Number of items on stack. count: INTEGER do Result := i end -- Number of items on stack. top: ¡G ACCOUNT do Result := imp [i] end -- Return top of stack. top: G do Result := imp [i] end feature -- Commands -- Return top of stack. feature -- Commands push (v: ¡G ACCOUNT ) do imp[i] := v; i := i +1end push (v: G ) do imp[i] := v; i := i +1end -- Add ’v’ to top of stack. -- Add ’v’ to top of stack. pop do i := i -1end pop do i := i -1end -- Remove top of stack. -- Remove top of stack. end end 17 of 33 19 of 33 Generic Collection Class: Client (1.1) Generic Collection Class: Client (2) As client, instantiate the type of G to be the one needed. As client, declaring ss: STACK[ STRING ] instantiates every G STRING 1 test_stacks: BOOLEAN occurrence of as . 2 local 3 ss: STACK[STRING] ; sa: STACK[ACCOUNT] class STACK [¡G STRING] 4 s: STRING ; a: ACCOUNT feature {NONE} -- Implementation 5 do imp: ARRAY[ ¡G STRING ];i: INTEGER 6 ss.push("A") feature -- Queries 7 ss.push(create {ACCOUNT}.make ("Mark", 200)) count: INTEGER do Result := i end 8 s := ss.top -- Number of items on stack.
Details
-
File Typepdf
-
Upload Time-
-
Content LanguagesEnglish
-
Upload UserAnonymous/Not logged-in
-
File Pages9 Page
-
File Size-