Intro to Software Engineering - Module Design

908 views

Published on

Published in: Technology, Education
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
908
On SlideShare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
15
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Intro to Software Engineering - Module Design

  1. 1. Module design McGill ECSE 321 Intro to Software Engineering Radu Negulescu Fall 2003
  2. 2. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 2 About this module There is a difference between software that relies on patches and hidden assumptions and software that is well crafted and well understood • The latter is easier to create and verify Here we discuss • Object design: filling the gap between high-level module interfaces and low-level platform • Design patterns: reusable micro-architectures • Assertions: intellectual tools for understanding code, avoiding inconsistencies, and detecting defects • Effective usage of programming constructs: abstraction, inheritance, exceptions, polymorphism, block structure • Function-oriented design • User-interface design We do not discuss component-based design • See “Coding standards” module and ECSE 428
  3. 3. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 3 Object design Main challenge: many factors conspire to introduce inconsistencies in a large software system • Team development • Project duration • Defect repairs (!) • Multiple iterations • Redundancies For performance For user convenience
  4. 4. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 4 Identifying design objects First-cut: class names, responsibilities, and associations • Many of the classes will have just one object each • CRC cards: “class – responsibility – collaboration” • 6x4 inch cards Size, number of lines are deliberate
  5. 5. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 5 CRC cards • E.g. [Beck, Cunningham] Order Check items are in stock Order Line Determine the price Order Line Check for valid Customer payment Dispatch to delivery address
  6. 6. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 6 CRC example: ATM software [Beck, Cunningham] Account Keeps balance and traffic. Transaction RemoteDB Transaction Validates & performs money transfer. Keeps audit info. CardReader Dispenser RemoteDB Action Account Event Queues signals. Isolates H/W from user interface. Screen CardReader Dispenser RemoteDB CardReader Decodes strip. Signals insertion. Event Transaction Dispenser Emits cash. Signals success and empty. Event Transaction Screen Displays prompts. Dispatches Events to Actions. Event Action Action Sequences Screens. Assembles Transactions. Transaction Screen RemoteDB Retrieves Accounts. Records Transactions. Signals com status. Event Transaction Account Interface FSM Hello Another? Select Transaction Deposit Please Wait Withdrawal
  7. 7. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 7 Identifying design objects Typical design classes: • Refined entity classes Single analysis classes split into several design classes New attributes New methods • Objects from a class library or application framework • Lists of analysis objects • Generalized analysis classes • New control objects and boundary objects • Elements of design patterns
  8. 8. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 8 Elements of observer pattern Elements of observer pattern Refined analysis objects Refined analysis objects Identifying design objects Example: email auction EmailAdaptor sendMail receiveMail interpretMail AuctionSessionCtrl auctionState handleLogonCmd initiateAuction handleBidCmd timeout Timer delay reset(delay) wakeup() BidderLog addBidder removeBidder notifyAll Auction crtPrice compareBid setCrtBid getCrtBid BidderInfo emailAddr handleNotif Item initialPrice getPrice setPrice crtWinner 1 *1 * 1 sendMail handleCmd create 1 * timeout reset 1 * compareBid setCrtBid create 1 1 11 Message text addr getText setText getAddr setAddr 11 crtBid warning closure set,get receiveMail wakeup getCrtBid 1 Library class instance List
  9. 9. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 9 Identifying design objects Example: simple text editor • Keyboard, display (no mouse) • Insert, delete • Open, save • Find string Analysis yields • Entity: TextFile, Cursor, FindSelection • Boundary: Keyboard adaptor, Display adaptor • Control: FileCtrl, EditCtrl, FindCtrl Design yields • Entity: StringTable, StringPos - map strings to positions in text Design decision, assuming high ratio of find vs. edit • Boundary: Command (could also be entity or ctrl) • Control: UseCaseCtrl, ParseCtrl
  10. 10. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 10 Identifying design objects Generalized analysis class: handles Ctrl object associations uniformly Command list Library class instance: Hashtable of strings StringTable FindSelection KeyboardAdaptor DisplayAdaptor EditCtrl FileCtrl TextFile FindCtrl insertString removeString searchString ParseCtrl String pressedKeys readKeys parse Command String args[] execute UseCaseCtrl insertChar deleteChar moveCursor String searchString setSearchString getSearchString Cursor int position setPosition getPosition char image[][] refresh openFile saveFile search Hashtable 1 1 1 * 1 1 1 * * 1 * 1 * 1 * 1 * 1 1 1 * 1 * 1 * 1 StringPos *1 searchableString position New control object
  11. 11. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 11 Realizing associations Several options to implement associations • Tradeoff: maintainability vs. performance • Unidirectional vs. bi-directional • Multiplicity Unidirectional: • Use references • Collapse the target class into an attribute of the source class Bi-directional: • References both ways “Set” methods that update attributes in both classes • Or, collapse one class into an attribute of the other
  12. 12. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 12 Realizing associations Bi-directional associations present maintainability challenges • The set method of one class calls the set method of the other class [BD, fig.7.22]: MapArea -zoomIn:ZoomInAction +getZoomInAction() +setZoomInAction(action) ZoomInAction -targetMap:MapArea +getTargetMap() +setTargetMap(map) class MapArea extends JPanel { private ZoomInAction zoomIn; /* Other methods omitted */ void setZoomInAction (action:ZoomInAction) { if (zoomIn != action) { zoomIn = action; zoomIn.setTargetMap(this); } } } class ZoomInAction extends AbstractAction { private MapArea targetMap; /* Other methods omitted */ void setTargetMap(map:MapArea) { if (targetMap != map) { targetMap = map; targetMap.setZoomInAction(this); } } }
  13. 13. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 13 Realizing associations Association multiplicity • One-to-one, many-to-one, one-to-many, many-to-many • The “to-one” sides are easy to implement Reference field • The “to-many” sides can be implemented as a collection of references Array or Vector for ordered associations Hashtable for qualified associations Complex associations may justify implementations as separate objects
  14. 14. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 14 Realizing associations Analysis Design KeyboardAdaptor FileCtrl openFile closeFile 1 * KeyboardAdaptor DisplayAdaptor ParseCtrl String pressedKeys readKeys parse Command String args[] execute char image[][] refresh 1 1 1 * 1 1 1 * FileCtrl openFile closeFile UseCaseCtrl
  15. 15. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 15 Realizing associations Reducing multiplicity by qualifiers class Car { Vector parts; } class Car { Hashtable parts; } Car PartName Part 1 1 Car Part 1 * parts parts
  16. 16. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 16 Realizing associations Associations as separate objects E.g. [BD]: SimulationRun date author CPUtime getOutline() EmissionSource SimulationResult * 1 EmissionSource SimulationResult * 1 Object design model before transformation Object design model after transformation SimulationRun 1 1 date author CPUtime getOutline()
  17. 17. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 17 Object behavior State charts can help define the behavior of single objects, interactions with the actors, etc. • Justified for intricate objects, classes, or interfaces E.g. Applet life cycle: Inactive Active init start stop destroy
  18. 18. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 18 Adjusting library components Increasing reliance on code reuse • Class libraries • Application frameworks • Design patterns • ... Component selection and adaptation • Slight mismatches between library classes and application Change application class APIs if you can Might be hard to do because of other off-the-shelf components Write “glue code”: Adapter pattern
  19. 19. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 19 Design patterns Patterns in software engineering • “Reusable micro-architectures” Partial solutions to common software development problems • Distilled experience Patterns in object-oriented design • “A pattern is an abstraction of a collaboration” • Problem: constraints, optimization goals (“forces”) • Solution: small collection of classes and their relationships • Consequences: results and tradeoffs Beyond object-oriented design: • Testing patterns • Process patterns • CM patterns • …
  20. 20. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 20 Composite pattern Organizing recursive data structures Examples: • Recursive pictures • File and directory hierarchy Component Leaf Composite * DirectoryFile 0..1 Entry *
  21. 21. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 21 Composite pattern Note: • De-coupling of traversal and storage mechanisms • Multiplicity of the aggregation Some things are left unspecified Patterns are rarely encountered in pure form • Recursive implementation of each service E.g. move image
  22. 22. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 22 Example composite pattern Pictures in a document editor can be image files, text boxes, or contain other pictures recursively • Several possible structures are shown Combine inheritance and encapsulation Which has better modularity properties: cohesion, decoupling? ContainerTextBox 0..1 * Image 0..1 Picture Text,Image * Container TextBox 0..1 Picture * Image 0..1 Picture Text,Image * Picture
  23. 23. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 23 Observer pattern Decoupling model from views: Observer pattern • (a.k.a. Publisher-Subscriber) • Decouples model from views, as in the MVC architecture • However, does NOT decouple views from model for all s in subscriber list { s.update (); } Subject attach (Observer) detach (Observer) notify () Observer update () *1 ConcreteObserver observerState update () ConcreteSubject subjectState setState () getState () *1 subscriber publisher observerState = publisher.getState();
  24. 24. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 24 Observer pattern Examples: • Listeners in Java (not pure observer pattern) Discussion: • Decouple model from views, as in the MVC architecture • However, does NOT decouple views from model • A.k.a. Publisher-Subscriber
  25. 25. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 25 Proxy, strategy, adaptor, bridge patterns One generic implementation – several concrete implementations ConcreteImpl1 operation() GenericImplementation operation() ConcreteImpl2 operation() ...
  26. 26. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 26 Proxy, strategy, adaptor, bridge patterns Proxy: encapsulating expensive objects • A proxy object acts like a real object but calls real object methods • Applications: save expensive operations, security/firewall, ... Strategy: encapsulating algorithms • An abstract algorithm class provides API; concrete algorithm classes refine the abstract class and may be interchanged • Applications: decouple algorithm API from implementation Adapter: wrapping around legacy code • Glue code to encapsulate legacy code for use in a new system with new API Bridge: allowing for alternate implementations and alternate APIs • An “abstraction” class has a pointer to an “implementation” class • “Abstraction” and “Implementation” be refined/evolved independently • Applications: vendor independence, testing, ...
  27. 27. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 27 Abstract factory pattern Using graphical widgets regardless of the operating system • Shield an application from platform-specific classes • Each platform is represented by a specific factory class inheriting from the abstract factory class, that returns specific widgets inheriting from the abstract widgets classes Example: • Encapsulating windowing styles AbstractFactory createWindow() ... AbstractWindow MacWindowMotifWindow MotifFactory createWindow() ... MacFactory createWindow() ...
  28. 28. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 28 Command Enables encapsulation of control • Client/UI objects create and access command objects • Only command objects access server/entity objects • “History” stack Applications • Undo, redo, statistics • Allow future extension of command set • Decouple UI from functional ConcreteCmd1 execute() Command execute() ConcreteCmd2 execute() ... Client Server History *
  29. 29. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 29 Preparing for change Typical changes to prepare for: • Defect elimination • New implementation Performance-tuning New algorithm (e.g. game strategy change) • User interface New views required for usability New commands Alternate ways to input commands • Future extension or generalization E.g.: text editor system Change: support a new text format • New vendor or new technology E.g.: new security schemes • Changes in the application domain Business rules
  30. 30. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 30 Preparing for change Heuristics to make change easier: • Generalize Use delegation, inheritance, abstract classes • Modularize Encapsulate implementation details in routines and header files • Encapsulate variability Use certain design patterns E.g. decouple UI from implementation • Record rationale for design decisions • Design patterns can help Different vendor: Strategy pattern Extended scope: Command pattern Multiple views: Observer pattern
  31. 31. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 31 Mapping to hardware Determined by the following goals • Closeness to users, accessibility • Response time, e.g. for interrupt-driven systems Average-case Worst-case • To a lesser extent: throughput (because of overhead) Proxy pattern Server operation() ... ProxyServer operation() ... RealServer operation() ... Client
  32. 32. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 32 Example [BD]: :WebServer myMac:Mac :UnixHost :IExplorer aPC:PC :Database :UnixHost :Netscape
  33. 33. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 33 Reviewing the design Relate the design model to the specification (and the analysis model), by checking that: • Every subsystem can be traced to a requirement • Every design goal can be traced to a non-functional requirement • Every requirement is addressed Main checks (consistency, completeness, …): • Walkthroughs of use cases • Check boundary conditions and other system-wide policies • Check for conflicting design goals (which usually exist, but must be prioritized) Check that the design is optimal • “Local optimum”: check known/straightforward design alternatives No alternative yields a better goals tradeoff Particularly, check simplifications by removing or collapsing elements • Compare to similar designs
  34. 34. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 34 Reviewing the design Check feasibility (realism), by checking that: • All newly adopted technologies have been demonstrated/evaluated on the required platform • Performance has been estimated in presence of limiting factors, such as network congestion, concurrency control, etc. Use calibrated stubs Check readability, by looking for: • Appropriateness of each name (see naming guidelines) • Consistency of style and levels of description • Naming conflicts
  35. 35. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 35 Assertions Specify subsystem services by means of assertions Natural language Mathematical logic Object constraint language (OCL) Formal specification languages, such as Z ... Other benefits • Reasoning tools to determine whether the code works • Instrument the code to automatically check assertions at run-time
  36. 36. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 36 Assertions An assertion is a logic predicate on program variables. E.g.: • “Integer i is greater than 0” • “Array a is sorted in ascending order” Or, equivalently, “for all i < j, we have a[i] < a[j]” • “String s contains only lowercase letters” Not an assertion: • “Object o is an instance of class C” • “Function foo returns an integer”
  37. 37. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 37 Post-conditions Assertions provide a means to specify what a piece of software is supposed to do Example: find the highest grade in a class max = 0; for (i = 0; i < n; i ++) { if (max < grade[i]) { max = grade[i]; } } • We’ll use this as a running example, not necessarily a good design Should start with max = - infinity What we want the code to ensure upon termination can be stated as an assertion, called post-condition: (∀ k ∈ {0,..,n - 1}: max ≥ grade[k]) ∧ (∃ k ∈ {0,..,n - 1}: max = grade[k]) • This reads: max must be greater than all grades, and equal to some of them
  38. 38. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 38 Pre-conditions However, our code snippet has some hidden assumptions • For instance, it will not ensure the post-condition if all grades are negative • What is the computed max in that case? What the code assumes can also be stated as an assertion, called pre- condition For our code snippet, a possible pre-condition is: (n > 0) ∧ (∀ k ∈ {0,..,n - 1}: grade[k] ≥ 0) • This reads: the grade array is non-empty, and all grades are non- negative The pre-condition/post-condition pair constitutes a specification: • The snippet computes the maximum if all grades are non-negative
  39. 39. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 39 Correctness conditions What does it mean that a program is “correct”? • Sequential program: one stream of execution • Parallel program: several streams running at the same time and synchronizing on certain events • For a parallel program, there is a complex range of failures, subject to on-going research For a sequential program, there are two criteria: • Termination: the program will eventually terminate its execution • Partial correctness: if the program terminates, the results satisfy the post-condition • Termination + partial correctness = total correctness • These criteria need to hold only for those combinations of input parameters that meet the program’s pre-condition
  40. 40. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 40 Correctness conditions Example: attempt to deal with negative grades max = 0; for (i = 0; i < n; i ++) { if (grade[i] < 0) { max = grade[i]; } if (max < grade[i]) { max = grade[i]; } } Will this work?
  41. 41. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 41 Correctness conditions If there are negative grades, the program on the previous slide may or may not determine the correct maximum Suppose: n = 3 grade[] contains -10, -55, 78 Then: max takes values 0, -10, -55, 78 (GOOD) i takes values 0, 1, 2, 3 Suppose: n = 3 grade[] contains 92, -55, 78 Then: max takes values 0, 92, -55, 78 (BAD) i takes values 0, 1, 2, 3
  42. 42. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 42 Correctness conditions However, the program on the previous slide is totally correct w.r.t. the following specification: Pre-condition: (n > 0) ∧ (∀ k ∈ {0,..,n - 1}: grade[k] ≥ 0) Post-condition: (∀ k ∈ {0,..,n - 1}: max ≥ grade[k]) ∧ (∃ k ∈ {0,..,n - 1}: max = grade[k]) In particular, the error mentioned above does not occur if all grades are positive, and the pre-condition grants that
  43. 43. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 43 Correctness conditions Still, the program on the previous slide is not correct w.r.t. the following specification: Pre-condition: (∃ k ∈ {0,..,n - 1}: grade[k] ≥ 0) Post-condition: (∀ k ∈ {0,..,n - 1}: max ≥ grade[k]) ∧ (∃ k ∈ {0,..,n - 1}: max = grade[k]) This is because, as we have seen, an error may occur if some grades are negative
  44. 44. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 44 Routines To specify what a function or method is intended to do, document its pre- and post-conditions • Example: implement max by recursion /* assuming grade[0..i] non-negative, return the maximum of grade[0..i] */ int max( int *grade, /* student grades */ int i /* current student index */ ) ... /* main call */ yyy = max(grade, n - 1); ... {/* function max */ if (i >= 0) { int partMax; /* max of grade[0..i-1] */ partMax = max(grade, i - 1); if (partMax < grade[i]) { return grade[i]; } else { return partMax; } } else { return 0; } } /* end function max */
  45. 45. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 45 Routines E.g. JEWEL [B&D] maintains several superimposed map layers, which can can be zoomed and clipped. /** * Shows a map layer element at a given level of detail * and clipped to a given bounding box * pre: * - no two points have the same coordinates * - the level of detail is positive * - the bounding box width and height are positive * post: * - all returned points are within the specified bounding box * - all returned points are marked as viewable at the specified level * - all returned points are within the layer element */ Enumeration getOutline( Rectangle2D bbox, // bounding box for clipping double detail // zoom level )
  46. 46. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 46 Design by contract Contract between the caller and the callee • The callee eventually terminates and ensures the post-conditions if the caller ensures the pre-conditions • Normally, the class invariant is part of both pre- and post-condition for a method of an object of that class Not for constructors Meyer’s parable: consider a “restaurant” class with a “cook” method. The restaurant must be kept in a non-burning state; this is the “class invariant” • The cook assumes that food ingredients are available and the invariant holds when cooking starts (pre-condition), and ensures the food is eatable and the invariant is valid when cooking terminates (post-condition)
  47. 47. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 47 Defensive programming Instead of specifying pre-conditions, it is sometimes recommendable to make programs work for any parameter values If precondition is not met, cry foul: • Print error message • Return an error flag • Throw an exception • Fix the data • …
  48. 48. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 48 Example: design by contract vs. defensive prog. Draw a rectangle by dragging the mouse: int x1 = 0; int y1 = 0; // coordinates of mouse click int x2 = 0; int y2 = 0; // coordinates of mouse release ... public void paint(Graphics g) { // Determine the upper left corner int startx = Math.min(x1, x2); int starty = Math.min(y1, y2); int width = Math.abs(x2 - x1); int height = Math.abs(y2 - y1); // Draw the rectangle g.drawRect(startx, starty, width, height); }
  49. 49. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 49 Tradeoff Reliability: Defensive programming provides an extra level of fault detection and protection against invalid results Maintainability: Design-by-contract eliminates redundancies and produces tighter and leaner code, easier to maintain Recommendation: • Design by contract is preferred for most internal modules and custom-written modules Less overhead, less redundancy • Defensive programming is required at the external boundary of the system (user interface, communication modules), and for reusable software modules Have a defined response for each misuse
  50. 50. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 50 Class invariants Consider a class that keeps track of student records: class Record { public static final int LIM = 100; // upper limit private int n; // number of students on record private int grade[] = new int[LIM]; // grades list ... Suppose the methods of the class make sure that: • There is at least one grade • There are less than 100 grades • All grades are non-negative A class invariant is a property that holds throughout the lifetime of each object of a class, after each method is executed • “Data invariant”, “rep invariant” [Liskov], ... • A class invariant can be expressed as an assertion: 0 < n < 100 and (∀ k ∈ {0,..,n - 1}: grade[k] ≥ 0)
  51. 51. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 51 Class invariants Each method of the class may assume the invariant holds • Part of the pre-condition of the method E.g. since max() does not have arguments, its pre-condition is in terms of the object fields (∃ k ∈ {0,..,n - 1}: grade[k] ≥ 0) Since n > 0, there is at least one non-negative grade stored. Hence the pre- condition of max() is satisfied Each method of the class must preserve the invariant • Part of the post-condition ensures the invariant holds For max this is easy, since max does not modify n and does not modify the entries in grade either. Assuming the invariant holds when max starts, the invariant also holds when (and if) max terminates.
  52. 52. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 52 Class invariants Let's consider another method in the Record class ... /* Update the grade of a student * pre: 0 <= student < n and 0 <= newGrade * post: grade[student] = newGrade */ public void insert( int student, // student whose grade is updated int newGrade // the new value of that grade ) { if (newGrade < 0) { grade[student] = 0; } else { grade[student] = newGrade; } } ...
  53. 53. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 53 Class invariants When it terminates, insert() ensures: • Its own side effect, that the grade of student has value newGrade (if that is a valid grade) • The validity of the class invariant after the method finishes The insert() method can also: • Assume that the invariant holds before insert is executed • Demand its own pre-conditions: student within range, valid newGrade
  54. 54. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 54 Example: hashtable [Bruegge and Dutoit] <<invariant>> self.numElements >= 0 HashTable put(key,entry:Object) get(key):Object remove(key:Object) containsKey(key:Object):boolean size():int numElements:int <<postcondition>> !containsKey(key) <<postcondition>> get(key) == entry <<precondition>> containsKey(key) <<precondition>> !containsKey(key) <<precondition>> containsKey(key)
  55. 55. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 55 Example: hashtable class Hashtable { /* The number of elements in the Hashtable is nonnegative at all times. * @inv numElements >= 0 */ private int numElements; /* The put operation assumes that the specified key is not used. * After the put operation is completed, the specified key can be used * to recover the entry with the get(key) operation: * @pre !containsKey(key) * @post containsKey(key) * @post get(key) == entry */ public void put (Object key, Object entry) {…}; /* The get operation assumes that the specified key corresponds to an * entry in the Hashtable. * @pre containsKey(key) */ public Object get(Object key) {…}; /* The remove operation assumes that the specified key exists in the * Hashtable. * @pre containsKey(key) * @post !containsKey(key) */ public void remove(Object key) {…}; /* Other methods omitted */ }
  56. 56. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 56 Example: map zoom levels JEWEL [BD] memorizes for each point feature of a map the levels at which that point is viewable or non-viewable The following class invariants are maintained: • No two points have the same coordinates (This is needed in the precondition of the getOutline method) • For each point, the sets of levels at which that point is viewable and non-viewable are disjoint
  57. 57. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 57 Example: robot arm Design a system for planning the robot arm movements • Keeps track of arm segments and external bodies • Stores shape and position information • Shapes are approximated by triangle polyhedra • Issues commands to arm engines Invariants • Triangles in a shape are contiguous • Body and segment surfaces are closed • All shapes are disjoint • Engine commands do not overlap in time for the same engine
  58. 58. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 58 Constructors Class constructors have a different contract than other methods • Cannot assume the invariant as pre-condition • Must set the class attributes so the invariant holds initially • Example: /** * inv: 0 < n < 100 and all grades are non-negative */ class Record { public static final int LIM = 100; // upper limit private int n; // number of students on record private int grade[] = new int[LIM]; // grades list ... public Record() { n = 1; grade[0] = 0; } ... }
  59. 59. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 59 Inheritance A class invariant should be inherited together with the data fields • E.g. replace the record class by one that assumes there are some grades greater than 50 (to save some re-assignments of currentMax) /** * inv: 0 < n < 100, all grades are non-negative, * and some grades are >= 50. */ class Clone extends Record { public static final int LIM = 100; // upper limit private int n; // number of students on record private int grade[] = new int[LIM]; // grade list public int max() { int currentMax = 50; for (int i = 0; i < n; i ++) { if (currentMax < grade[i]) { currentMax = grade[i]; } } return currentMax; } ... }
  60. 60. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 60 Loop invariants Loop invariants are assertions that hold whenever a certain point in the loop is reached • Example: variable max always contains the highest grade considered max = 0; for (i = 0; i < n; i ++) { if (max < grade[i]) { max = grade[i]; } /* here, max == largest item in grades[0..i] */ } More generally, an invariant is a local expression in program variables that does not change during the execution of the program • May or may not be an assertion • It has to be about the values of the program variables • Local: valid at certain points only; might be updated elsewhere The invariant is valid whenever this point is reached
  61. 61. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 61 Iteration vs. recursion max = 0; for (i = 0; i < n; i ++) { if (max < grade[i]) { max = grade[i]; } /* here, max == largest item in grades[0..i] */ } /* @post: return value == largest item in grades[0..i] */ int max( int i, int *grade ) { int partmax; if (i != 0) { partmax = max(i – 1, grade); if (partmax < grade[i]) { return grade[i]; } else { return partmax; } } else { return 0; } }
  62. 62. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 62 Analyzing partial correctness To establish partial correctness: • Assume the pre-condition holds • Then the invariant holds before the first iteration (induction basis) • Prove the invariant is preserved by each iteration (induction step) • Thus the invariant holds after the last iteration • Finally, the invariant entails the post-condition pre-condition ⇒ invariant(0) ⇒ invariant(1) ⇒ ... ⇒ invariant(n-1) ⇒ post-condition • Example: max grade • Recursive case: replace invariant by post-condition of recursive call
  63. 63. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 63 Example: designing algorithms Example: The Bellman-Ford algorithm for finding shortest paths • Variables: init = initial node, the origin of the paths cost[n1][n2] = cost of walking the edge (n1, n2) from node n1 to node n2 (considered to be infinite if there is no such edge) dist[n] = cost of the minimum cost path (distance) from node init to node n numNodes = number of nodes • Algorithm: for each node n dist[n] = + infinity (largest number represented by the machine); dist[init] = 0; for i = 1 to numNodes – 1 for each edge (n1, n2) dist[n2] = min(dist[n2], dist[n1] + cost(n1, n2)); Invariant: dist[n2] = smallest cost on paths of length ≤ i from init to n2 • Basis: i = 0: dist[init] = 0 • Step: if dist[n1] satisfies inv(i-1) then dist[n2] will satisfy inv(i)
  64. 64. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 64 Analyzing partial correctness Often, structural (non-linear) induction • Iterate over other bounded structures (trees, DAGs, ...) • Similar to linear case: basis, step E.g. prove that #files = #entries – #directories – #links + #partitions • Basis: 1 partition, 0 files, 1 directory (root), 0 links 0 = 0 – 1 – 0 + 1 • Assume (hyp): #files = #entries – #directories – #links + #partitions • Step: new partition => new root directory Hyp. => #files = #entries – (#directories + 1) – #links + (#partitions + 1) • Step: new file => new directory entry Hyp. => (#files + 1) = (#entries + 1) – #directories – #links + #partitions • Step: new directory => new directory entry Hyp. => #files = (#entries + 1) – (#directories + 1) – #links + #partitions • Step: new link => new directory entry Hyp. => #files = (#entries + 1) – #directories – (#links + 1) + #partitions
  65. 65. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 65 Quicksort algorithm /* post: sorted; pre: none */ quicksort ( M..N ) { if (M<N) { pick an element as “pivot” copy all smaller elements to lower locations (from M upwards) copy all larger elements to higher locations (from N downwards) copy pivot to remaining location P quicksort (M..P-1) quicksort (P+1..N) } } Example: proving correctness M P N … … ≤ pivot pivot ≥ pivot 3 2 1 1 4 2 5 8 9 7 5 6 6 7
  66. 66. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 66 Example: proving correctness Proving the post-condition by structural induction: • Step: Assume after recursive calls the sublists M..P-1 and P+1..N are sorted After copy, each element in sublist M..P-1 is smaller than the element at P After copy, the element at P is smaller than each element in sublist P+1..N Then the whole M:N list is sorted • Basis: If M == N then the list has 1 element If M > N then the list has 0 elements 1- and 0-element lists are always sorted M P N … … ≤ pivot pivot ≥ pivot 3 2 1 1 4 2 5 8 9 7 5 6 6 7
  67. 67. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 67 Programming constructs Support for structured programming • Procedural abstraction • Block hierarchy Support for object-oriented programming • Data / object abstraction • Inheritance • Exceptions • Polymorphism • …
  68. 68. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 68 Effective use of abstraction The interface of a software module should hide implementation details • Give a black-box view: make visible features that are interesting from the viewpoint of the clients of the class Good interfaces set the stage for good implementation and testing • Define consistent services • Define independent (“orthogonal”) services • Define generic services and special services • Exploit all symmetries and similarities • Assimilate special cases Treat extreme values just like typical values
  69. 69. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 69 Example: using abstraction Design a program for designing T-shirts • Entity classes: front, back, sleeve, collar, picture; sowing, fabric sheet, cut, texture, cut base • Other: user commands (e.g. stretch operation), interface widgets A TshirtSleeve class can wall off part of the system state: • Shape parameters: cut base, length, width, etc. • Handle points: corners, sides, center; focus Services offered: • Generic operations: getLength(), getHandle(index), moveHandle(...), setHandleFocus(index), draw() Orthogonality: isolate drawing from data access, getters and setters, etc. Consistency: deal with different types of handles uniformly Generic access to attributes: getX, setX • Specific operations: getSleeveArea, getUseRatio • Generic access to specific attributes: getCutBase
  70. 70. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 70 Example: using abstraction Example: the interface of a random number generator shows the random number but hides the randomization algorithm /* The Random class from java.util */ Random { protected int next(int b) {... ... public double nextDouble() {... public float nextFloat() {... public int nextInt() {... ... setSeed(long rnd) {... ... }
  71. 71. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 71 Example: using abstraction Assimilate special cases • E.g. GCD algorithm Divide with remainder, then switch roles Ancient Greeks didn’t treat 1 as a number! More complex GCD algorithm Many exceptional cases • E.g. max algorithm should allow i = 0 max = - infinity; for (i = 0; i < n; i ++) { if (max < grade[i]) { max = grade[i]; } } Interesting properties hold consistently even for empty range max(max(M..P), max(P..N)) = max(M:N) • E.g. the empty dataset is the most common result of a SQL query! • Ever wondered why counting from 0 caught on? Closeness to the machine (C) Consistent properties! (Dijkstra)
  72. 72. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 72 Effective use of inheritance The purpose of class generalization is to avoid redundant maintenance of common parts of several subclass objects Example: StretchableShape • Generic parameters: length, width, coordinates • Generic handles: box corners, box sides • Mouse listener and mouse movement listener • Method for recomputing parameters after a handle move • Method for drawing the shape StretchableShape mouseDragged mousePressed moveHandle draw int length,width,x,y; Handle91 up,dn,left,right,ul, ur,dl,dr,focus move handle; redraw picture; } if a handle is in focus then { if clicked on a handle then { reassign focus; } else { focus = null; }
  73. 73. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 73 Effective use of inheritance Implementation inheritance: • Less general superclass • Considered bad style Undue coupling between the usage of the subclass and the details of the less general superclass Difficult refinement: can’t further refine the more general class without checking for hidden interactions with the fields of the less general class • Example: StretchableShape inherits TshirtSleeve Ignores the specific handles Overrides draw() to draw just the bounding box
  74. 74. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 74 Effective use of inheritance Interface inheritance (subtyping): • More general superclass • Considered good style Decouples the generic functionality from the specific Makes it easy to further refine both superclass and subclass (StretchableShape and TshirtSleeve) • Example: TshirtSleeve inherits StrechableShape Additional shape parameters and handles Method draw() draws the specific sleeve shape and handles in addition to the generic bounding box (stronger postcondition)
  75. 75. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 75 Implementation inheritance vs. delegation To we use the functions of a special class: delegate Example, after [B&D]: Hashtable put(Object, Object) get(Object):Object containsKey(Object):boolean containsValue(Object):boolean MySet insert(Object) extract(Object):Object contains(Object):boolean Hashtable put(Object, Object) get(Object):Object containsKey(Object):boolean containsValue(Object):boolean MySet insert(Object) extract(Object):Object contains(Object):boolean 1 1 Implementation inheritance (to be avoided): Delegation: (to be used instead):
  76. 76. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 76 Liskov’s substitution principle LSP: an object of the subclass can replace an object of the superclass in any context This defines proper subtyping • Weaker pre-conditions on method arguments • Stronger post-conditions on return results • Stronger class invariant
  77. 77. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 77 Subtyping example class Phasor { … /** * compute the angle of the phasor * pre: the phasor is non-zero; * post: within 10^(-5) of correct value */ public float angle(... … } class PrecisionPhasor extends Phasor { ... /** * compute the angle of the phasor * pre: none; * post: if the phasor is non-zero, * result is within 10^(-10) of correct value; * if the phasor is zero, the result is 0 */ public float angle(... … }
  78. 78. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 78 Subtyping and type extension Subtyping is compatible to type extension class Vehicle { private Position position; private Speed speed; ... getPosition (...) {...} setPosition (...) {...} turn (...) {...} start (...) {...} stop (...) {...} } Limitation: stronger pre-conditions may be needed for method arguments that belong to the subclass • Example: EmergencyVehicle overrides equals() in Vehicle • Example: Phasor may override add() in Float • LSP is inadequate here because the subclass object expects a method argument of the subclass: more fields, stronger invariant class EmergencyVehicle extends Vehicle { private int soundVolume; ... soundSiren (...) {...} }
  79. 79. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 79 Multiple inheritance Inherit features from several superclasses. Not allowed in Java classes because of possible postcondition conflicts • Java interface hierarchy permits multiple inheritance (implements) • No conflicts are possible between method signatures EmergencyVehicle Cistern soundSiren() loadLiquid() FireFighter class Base1 { public int foo () { return 1; } } class Base2 { public int foo () { return 2; } }
  80. 80. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 80 Effective use of exceptions Problem: deal with anomalous situations, such as hardware failures, wrong format of user input, off-range parameters, etc. Options: • Return a flag value (C) • Print an error message and perform a system exit • Throw an exception (Java) Example: int dayOfWeek(int year, int month, int dayOfMonth) • Assume year >= 1900, month >= 1, month <= 12, day within range • Different approaches: Return 0 for invalid parameters and 1-7 for valid If parameters are off range, print “error”; exit(0); If parameters are off range, throw an exception
  81. 81. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 81 Exception handling Exceptions handle errors by providing an alternate flow • Decouple (normal) execution flow from (alternate) exception flow • The caller can intercept exceptions thrown by the callee dayOfWeek(…) myCalendarMethod Print error message dayOfWeek(…) dayOfWeek(…) Normal execution. Exception handler. MyExc throw try { /* normal execution */ } catch (MyExc e) { /* exception handler */ }
  82. 82. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 82 Exceptions in Java Checked exceptions: need to be explicitly listed in method header (compiler-enforced) int dayOfWeek(...) throws ParamOffRangeException; Unchecked exceptions: not necessarily listed Throwable Exception RunTimeException (checked exceptions) (unchecked exceptions) Error
  83. 83. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 83 Exceptions in Java Usage • Indicate exception checking by inheriting the adequate supertype • Indicate what went wrong by passing a string public class MyNewException extends Exception { public MyNewException(String s) { super(s); } } ... Exception e1 = new MyNewException(“this is why”); ... String s = e1.toString(); /* here, s contains “this is why”*/
  84. 84. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 84 Example unchecked: invalid format public void read( ) { BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in)); try { n = Integer.parseInt(stdin.readLine()); for (int student = 0; student < n; student ++) { grade[student] = Integer.parseInt(stdin.readLine()); } } catch (NumberFormatException exception) { System.out.println(“Invalid input”); n = 1; grade[0] = 0; } catch (IOException exception) { System.out.println(“Input problem”); System.exit(0); } }
  85. 85. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 85 Example checked: valid but off-range parameters int dayOfWeek(...) throws ParamOffRangeException { if (year < 1900) { throw (new ParamOffRangeException(“Year off range”)); } if (month < 1 || month > 12) { throw (new ParamOffRangeException(“Month off range”)); } ... } Check params dayOfWeek … [valid] [not valid] Exception handler
  86. 86. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 86 Correctness of exception handlers The role of exception handlers (a.k.a. “catch clauses”) • Restore the invariant (when and if they terminate) Their post-conditions should be the class invariant However, sometimes the handler simply performs a system exit: Never “terminates” => no post-conditions! Examples The handler for NumberFormatException ensures a valid grades array The handler for IOException does not actually terminate. • Empty pre-conditions Assume nothing about the data if something abnormal has occurred • Meyer’s restaurant class has a “firefighter” exception handler Ensures the invariant holds when firefighting terminates (post-condition) Assumes nothing when firefighting begins (empty pre-condition)
  87. 87. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 87 Specifying exceptions For each parameter • Determine all values that should be avoided • Specify pre-condition • Specify exception to be thrown if pre-condition fails Does this effort pay off? • Time consuming, maintenance overhead • Limit exception specification to the interface of the system See defensive programming vs. design-by-contract tradeoff
  88. 88. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 88 Effective use of polymorphism Allow overloading (multiple bindings) of a name • poly = many; morph = form E.g. many methods with the same name but different signatures • Resolve the meaning at run-time The particular version of a method to be invoked is determined at run time because it depends on the type of the arguments Benefits • Avoids some dispatching, logical cohesion, control coupling • Simpler programs, facilitates change Introducing a new callee type does not require to change the caller • More intuitive naming Cost • Dynamic linking Extra overhead for making the method call
  89. 89. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 89 Polymorphism may replace decisions in the callee Example: a payroll system has different kinds of employees (part time, full time, contractor) whose paycheques are calculated differently Conventional implementation: int salary(Employee e) { if (e instanceof PartTimeEmployee) { return 50000; } if (e instanceof FullTimeEmployee) { return 100000; } } Implementation with polymorphism: int salary(PartTimeEmployee e) { return 50000; } int salary(FullTimeEmployee e) { return 100000; }
  90. 90. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 90 Polymorphism may replace decisions in the caller if (e instanceof PartTimeEmployee) { x = e.partTimeSalary(); } if (e instanceof FullTimeEmployee) { x = e.fullTimeSalary(); } int salary(PartTimeEmployee e) { return 50000; } int salary(FullTimeEmployee e) { return 100000; } Employee FullTimeEmployee fullTimeSalary() PartTimeEmployee partTimeSalary() … Caller Employee FullTimeEmployee salary() PartTimeEmployee salary() … Caller
  91. 91. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 91 Polymorphism vs. decisions in exception handling Example: try { ... } catch (NumberFormatException exception) { ... } catch (IOException exception) { ... }
  92. 92. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 92 Polymorphism vs. decisions in constructors Example: class Button { ... public Button() public Button(String str) ... } ... myFirstButton = new Button(“3”); mySecondButton = new Button();
  93. 93. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 93 Function-oriented design Typical function-oriented design can be viewed as processing a flow of data Incoming flow: • Read input, keyboard events, mouse events, phone tones, sensor events and status • Translate into internal format Outgoing flow: • Display output, print jobs, alarm, actuators • Translate from internal format
  94. 94. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 94 Types of data flow Transform flow: [Handout,fig.1] • Processing on internal format • Flow center, flow boundaries E.g., [Handout,fig.2] Sometimes, open to interpretation. • E.g. Spice: Enter schematics, models, initial values Simulate Output waveform
  95. 95. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 95 Types of data flow Transaction flow: [Handout,fig.3] • Transaction center: selects and triggers one of many "action paths" E.g., [Handout,fig.4] • E.g. command interpreter: Input command Parse command Invoke corresponding application routine
  96. 96. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 96 Determining program structure Determine program structure: • Establish type of information flow (transaction or transform) In the case of transform flow, identify flow “center” and boundaries In the case of transaction flow, identify transaction center • Map DFD into program structure Transform flow: 1st level factoring: input/processing/output E.g. [Handout,fig.5] 2nd level factoring: map bubbles into modules Start from center E.g. [Handout,fig.6] Transaction flow: Dispatch modules and worker modules Factor each “action path” using transform mapping • Refine resulting structure E.g. [Handout,fig.7]; compare to [Handout,fig.8]
  97. 97. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 97 Procedural design When reaching a low enough level of module decomposition, design the interface and algorithm for each module or function Procedure interface: • Signature: argument and return types • Pre- and post-conditions Algorithms can be built from a few basic constructs: • Sequence • Condition • Repetition • (Fork/join construct for concurrent programs) • “Goto” is not needed. “Chunking” improves understanding Algorithm notations: • PDL • Graphical design notations
  98. 98. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 98 Designing algorithms See if you can use an algorithm design technique: • Greedy / heuristic: go for the maximum local gain E.g. packing algorithm that fits items in a minimum number of boxes: get the largest item, then the largest remaining item, etc. Might not get to optimum packing, but usually good enough • Backtracking: try again If optimum is not obtained, try another choice E.g. skip largest item at one step in packing algorithm • Divide et impera: solve subproblems first E.g. quicksort: sort sublists first • ...
  99. 99. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 99 Designing algorithms Work within a constraint/idea • E.g. Quicksort: separate greater from smaller items w.r.t. pivot • E.g. bubble sort: swap consecutive items Analyze the algorithm using invariants • Or, use recursive call post-conditions instead of invariants • E.g. Quicksort: sublists are sorted on recursive call return Analyze scalability of the algorithm using big-Oh notation • Polynomial time = good
  100. 100. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 100 Example: designing algorithms Example: The Bellman-Ford algorithm for finding shortest paths • Variables: init = initial node, the origin of the paths cost[n1][n2] = cost of walking the edge (n1, n2) from node n1 to node n2 (considered to be infinite if there is no such edge) dist[n] = cost of the minimum cost path (distance) from node init to node n numNodes = number of nodes • Algorithm: for each node n dist[n] = largest number represented by the machine; dist[init] = 0; for i = 1 to numNodes – 1 for each edge (n1, n2) dist[n2] = min(dist[n2], dist[n1] + cost(n1, n2)); Technique: Greedy Idea: consider layers of paths of length i around init node Invariant: dist[n2] = smallest cost to n2 on all paths of length ≤ i Scalability: runtime = O(#nodes) * O(#edges)
  101. 101. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 101 Designing algorithms Usually, a “brute force” solution is readily available • Backtrack over combinations of solution elements • Check if current combination solves the problem • Exponential running time Sometimes, that’s good enough Often, that’s not good enough A clever solution has polynomial running time • Might be difficult to see A greedy or heuristic solution might be good enough and easy to see • Probably close to optimum: packing algorithm • Always optimum: Bellman-Ford
  102. 102. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 102 Effective use of program control blocks An algorithm can be made of nested block structures • Nesting minimizes impact of change • Dijkstra: “Goto statement considered harmful” CACM, 1968 Controversial: ““Goto considered harmful” considered harmful” Proven by practice...
  103. 103. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 103 Effective use of program control blocks Sequence (straight-line code) • Minimize “windows of vulnerability” of variables Selection (if-then, if-then-else, switch-case) • Branch the common case first (less volatile) • Encapsulate complex condition checks (decouple) • Avoid dropping through the end of a case Repetition (while, do, for) • Prefer loops with initial test (while, for) • Use loop-with-exit (break) to avoid redundancy in code (one loop and a half) • Place initialization code just before the loop body (proximity) • Keep loops “single minded” (like routines) • Check extremes: first case, last case, zero-iterations, ... Routine calls • Minimize number of return statements
  104. 104. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 104 Example: using program control blocks Word counter: a word is a sequence of non-blank characters status = “out-word”; wordCount = 0; while(not end-of-file) { read character; switch (status) { case “in-word”: if (blank character) { wordCount ++; status = “out-word”; } break; case “out-word”: if (non-blank character) { status = “in-word”; } break; } } Avoid “dropping through the end” Initial test loop. Deals with extreme case: empty file! Initialize vars just before the loop Branch common case first
  105. 105. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 105 Example: using program control blocks Checking extremes: • What happens at end of file? If no trailing blank? • Quick fix: if (status == “in-word”) { wordCount ++; status = “out-word”; }
  106. 106. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 106 Example: using program control blocks Better solution: avoid redundancy! status = “out-word”; wordCount = 0; while(true) { read character; switch (status) { case “in-word”: if (blank character or end-of-file) { wordCount ++; status = “out-word”; if (end-of-file) goto ENDLOOP; } break; case “out-word”: if (non-blank character) { status = “in-word”; } break; } } ENDLOOP: Loop-with-exit Why, isn’t this “spagetti code”?
  107. 107. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 107 Comparison to OO Where do we want function-oriented design? • "A functional approach to design is therefore most likely to be successful when the amount of system information is minimized and information sharing is explicit." [Sommerville, p. 276] E.g., many business processing systems have one central database and various transactions that can be performed independently of each other. E.g., many scientific or technical applications (Spice) have one central repository and can perform independent transformations on it Usually, a combination of the two approaches is optimal • Using an OO programming language usually results in a combination of OO and FO designs • Some non-OO languages allow elements of OO design E.g. C “headers”
  108. 108. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 108 User interface design UI types • Menu-based • Control panel • Command line Usage • Pull-down menu: a selection of all tasks • Toolbar, pop-up menus: quick access to frequently accessed tasks • Command line and shorthands: power users
  109. 109. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 109 User-interface design UI types
  110. 110. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 110 Control panel UIs Also known as: dialog box, touch pad Elements: buttons, checkboxes, radio buttons, toolbars, scrollbars, textboxes, lists, ... Advantages: direct manipulation, compactness Disadvantages: less structure, harder to design and program
  111. 111. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 111 Menu based UIs Elements: menu bar, menu, menu item, separator Types: • Pull-down, pop-up • Scroll menus • Hierarchical menus / walking menus / tree map • Associated control panels Advantages: easy learning, no typing effort, avoid some user errors, allow context help Disadvantages: hard to find a command, large access time
  112. 112. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 112 Command line UIs Advantages: • Relatively easy to develop, flexible • Rapid access for power users Disadvantages: steep learning curve, user errors, less appealing output
  113. 113. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 113 Types of UIs Various combinations are common • Advantages and disadvantages for each of the following?
  114. 114. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 114 UI design guidelines Ease of use • Support both novices and experts Frequent things should be easy, complex things should be possible E.g. shorthands for frequent actions, customization for complex actions Offer alternate commands (e.g. pop-up menus) • Flow of screens should match the flow of work Storyboarding • Use short, intuitive command labels • Open windows in the center of action
  115. 115. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 115 UI design guidelines Ease of learning • Help users get a system perception consistent with the system image System perception (user's model): mental model formed by the users about interacting with the system System image: how the system actually appears to the users • Resemble manual tasks Task analysis • Minimize memorization, dialogues, motion, etc Reduce memory load on users Reduce number and complexity of commands Reduce length and complexity of system spec Use colors to communicate emphasis, not content; design for black-and- white, then add color • Grouping of commands/menus: consistent with activities, use cases • Style and collaterals (help, error messages, etc) • Consistency, consistency, consistency
  116. 116. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 116 UI design guidelines Feedback • Confirmation of destructive actions • Timing: Less than 0.1s: appears instantaneous Over 1s: hourglass • Use analog displays for relative sizes: dangerous levels, growth • Use numerical displays for absolute sizes or precise sizes Documentation, help • Completeness • Ease of access, on-line availability • Structure • Customized and context-sensitive help • Tutorials
  117. 117. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 117 UI design guidelines Error tolerance and recovery (forgive mistakes) • Helpful error messages Problem-oriented Constructive (indicate repair action) Indicate consequences Non-judgmental May contain visual and audible cues • Undo, redo • Disable improper commands Context-dependent • Allow skipping redundant info (units of measure, .00, etc) • System response time Duration Predictability
  118. 118. McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 118 References Object design • BD 7.1-7.3, 7.4.1-7.4.5, 7.4.8-7.4.14 Design patterns • BD Appendix A Assertions and invariants • BD 7.3.3 • Liskov ch. 5 Programming constructs • Liskov ch. 3, 4 • McConnell ch. 13, 14, 15 User interface design • Sommerville ch. 15

×