Date post: | 07-Sep-2014 |
Category: |
Technology |
Upload: | radunegulescu |
View: | 588 times |
Download: | 1 times |
Module design
McGill ECSE 321Intro to Software Engineering
Radu Negulescu
Fall 2003
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
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
• RedundanciesFor performanceFor user convenience
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 cardsSize, number of lines are deliberate
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 5
CRC cards
• E.g. [Beck, Cunningham]
OrderCheck items are in stock Order LineDetermine the price Order LineCheck for valid CustomerpaymentDispatch to deliveryaddress
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.
TransactionRemoteDB
Transaction
Validates & performs money transfer.
Keeps audit info.
CardReaderDispenserRemoteDBActionAccount
Event
Queues signals.
Isolates H/W from user interface.
ScreenCardReaderDispenserRemoteDB
CardReader
Decodes strip.Signals insertion.
EventTransaction
Dispenser
Emits cash.Signals success and empty.
EventTransaction
Screen
Displays prompts.Dispatches Events to Actions.
EventAction
Action
Sequences Screens.Assembles
Transactions.
TransactionScreen
RemoteDB
Retrieves Accounts.Records Transactions.Signals com status.
EventTransactionAccount
Interface FSM
Hello
Another?
SelectTransaction
Deposit
Please Wait
Withdrawal
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 7
Identifying design objects
Typical design classes:
• Refined entity classesSingle analysis classes split into several design classesNew attributesNew 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
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 objectsRefined analysis objects
Identifying design objects
Example: email auction
EmailAdaptor
sendMailreceiveMailinterpretMail
AuctionSessionCtrlauctionStatehandleLogonCmdinitiateAuctionhandleBidCmdtimeout
Timerdelayreset(delay)wakeup()
BidderLog
addBidderremoveBiddernotifyAll
AuctioncrtPricecompareBidsetCrtBidgetCrtBid
BidderInfoemailAddrhandleNotif
IteminitialPricegetPricesetPrice
crtWinner
1
*1
*1
sendMail
handleCmd
create
1 *
timeout
reset
1 *
compareBidsetCrtBid
create
1 1
11
MessagetextaddrgetTextsetTextgetAddrsetAddr
11
crtBidwarningclosure
set,get
receiveMail
wakeup
getCrtBid
1
Library class instance
List
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 textDesign decision, assuming high ratio of find vs. edit
• Boundary: Command (could also be entity or ctrl)
• Control: UseCaseCtrl, ParseCtrl
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: Hashtableof strings
StringTable
FindSelection
KeyboardAdaptor
DisplayAdaptor
EditCtrl
FileCtrl
TextFile
FindCtrl
insertStringremoveStringsearchString
ParseCtrlString pressedKeysreadKeysparse
CommandString args[]execute
UseCaseCtrl
insertChardeleteCharmoveCursor
String searchStringsetSearchStringgetSearchString
Cursorint positionsetPositiongetPosition
char image[][]refresh
openFilesaveFile
search
Hashtable
1 1 1 *
1
1
1 *
*1
*
1
* 1
* 1
*
1
11
*
1
*
1
*
1
StringPos*1
searchableStringposition
New control object
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
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);
}}
}
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 implementReference field
• The “to-many” sides can be implemented as a collection of referencesArray or Vector for ordered associationsHashtable for qualified associations
Complex associations may justify implementations as separate objects
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 14
Realizing associations
Analysis
Design
KeyboardAdaptor FileCtrl
openFilecloseFile
1 *
KeyboardAdaptor
DisplayAdaptor
ParseCtrlString pressedKeysreadKeysparse
CommandString args[]execute
char image[][]refresh
1 1 1 *
1
1
1 *
FileCtrl
openFilecloseFile
UseCaseCtrl
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 Part1 1
Car Part1 *
parts
parts
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
dateauthorCPUtimegetOutline()
EmissionSource SimulationResult* 1
EmissionSource SimulationResult* 1
Object design model before transformation
Object design model after transformation
SimulationRun
1 1
dateauthorCPUtimegetOutline()
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
stopdestroy
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 applicationChange application class APIs if you can
Might be hard to do because of other off-the-shelf componentsWrite “glue code”: Adapter pattern
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
• …
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
*
DirectoryFile0..1
Entry*
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 aggregationSome things are left unspecifiedPatterns are rarely encountered in pure form
• Recursive implementation of each serviceE.g. move image
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 shownCombine inheritance and encapsulationWhich has better modularity properties: cohesion, decoupling?
ContainerTextBox0..1
*
Image
0..1
Picture
Text,Image
*
Container
TextBox
0..1Picture *
Image
0..1
Picture
Text,Image
*
Picture
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
ConcreteObserverobserverStateupdate ()
ConcreteSubjectsubjectStatesetState ()getState ()
*1
subscriber
publisher
observerState = publisher.getState();
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
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()
...
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, ...
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()...
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*
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 implementationPerformance-tuningNew algorithm (e.g. game strategy change)
• User interfaceNew views required for usabilityNew commandsAlternate ways to input commands
• Future extension or generalizationE.g.: text editor systemChange: support a new text format
• New vendor or new technologyE.g.: new security schemes
• Changes in the application domainBusiness rules
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 30
Preparing for change
Heuristics to make change easier:
• GeneralizeUse delegation, inheritance, abstract classes
• Modularize Encapsulate implementation details in routines and header files
• Encapsulate variabilityUse certain design patternsE.g. decouple UI from implementation
• Record rationale for design decisions
• Design patterns can helpDifferent vendor: Strategy patternExtended scope: Command patternMultiple views: Observer pattern
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 systemsAverage-caseWorst-case
• To a lesser extent: throughput (because of overhead)
Proxy pattern
Server
operation()...
ProxyServer
operation()...
RealServer
operation()...
Client
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 32
Example
[BD]:
:WebServermyMac:Mac :UnixHost
:IExploreraPC:PC
:Database:UnixHost
:Netscape
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 alternativesNo alternative yields a better goals tradeoffParticularly, check simplifications by removing or collapsing elements
• Compare to similar designs
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
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 35
Assertions
Specify subsystem services by means of assertionsNatural languageMathematical logicObject 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
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”
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 classmax = 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 designShould 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
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
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 inputparameters that meet the program’s pre-condition
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?
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 = 3grade[] contains -10, -55, 78
Then:max takes values 0, -10, -55, 78 (GOOD)i takes values 0, 1, 2, 3
Suppose:n = 3grade[] contains 92, -55, 78
Then:max takes values 0, 92, -55, 78 (BAD)i takes values 0, 1, 2, 3
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
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
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 */
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 clippingdouble detail // zoom level
)
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 ifthe 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)
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
• …
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 clickint x2 = 0; int y2 = 0; // coordinates of mouse release
...
public void paint(Graphics g) {
// Determine the upper left cornerint 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 rectangleg.drawRect(startx, starty, width, height);
}
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 reusablesoftware modules
Have a defined response for each misuse
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 limitprivate int n; // number of students on recordprivate 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)
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 methodE.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 holdsFor 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.
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 updatedint newGrade // the new value of that grade
) {if (newGrade < 0) {grade[student] = 0;
} else {grade[student] = newGrade;
}}...
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
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):Objectremove(key:Object)containsKey(key:Object):booleansize():int
numElements:int
<<postcondition>>!containsKey(key)
<<postcondition>>get(key) == entry
<<precondition>>containsKey(key)
<<precondition>>!containsKey(key)
<<precondition>>containsKey(key)
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 55
Example: hashtableclass 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 */}
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
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
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 limitprivate int n; // number of students on recordprivate int grade[] = new int[LIM]; // grades list...public Record() {
n = 1;grade[0] = 0;
}...
}
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 limitprivate int n; // number of students on recordprivate 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;
}...
}
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
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;
}}
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
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 pathscost[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 nnumNodes = 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)
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 links0 = 0 – 1 – 0 + 1
• Assume (hyp): #files = #entries – #directories – #links + #partitions
• Step: new partition => new root directoryHyp. => #files = #entries – (#directories + 1) – #links + (#partitions + 1)
• Step: new file => new directory entryHyp. => (#files + 1) = (#entries + 1) – #directories – #links + #partitions
• Step: new directory => new directory entryHyp. => #files = (#entries + 1) – (#directories + 1) – #links + #partitions
• Step: new link => new directory entryHyp. => #files = (#entries + 1) – #directories – (#links + 1) + #partitions
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 Pquicksort (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
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 sortedAfter copy, each element in sublist M..P-1 is smaller than the element at PAfter copy, the element at P is smaller than each element in sublist P+1..NThen the whole M:N list is sorted
• Basis: If M == N then the list has 1 elementIf M > N then the list has 0 elements1- 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
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
• …
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 casesTreat extreme values just like typical values
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 uniformlyGeneric access to attributes: getX, setX
• Specific operations: getSleeveArea, getUseRatio
• Generic access to specific attributes: getCutBase
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) {......
}
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 71
Example: using abstraction
Assimilate special cases
• E.g. GCD algorithmDivide with remainder, then switch rolesAncient Greeks didn’t treat 1 as a number!
More complex GCD algorithmMany exceptional cases
• E.g. max algorithm should allow i = 0max = - infinity;for (i = 0; i < n; i ++) {
if (max < grade[i]) {max = grade[i];
}}
Interesting properties hold consistently even for empty rangemax(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)
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
mouseDraggedmousePressedmoveHandledraw
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;
}
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 styleUndue 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 TshirtSleeveIgnores the specific handlesOverrides draw() to draw just the bounding box
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 styleDecouples the generic functionality from the specificMakes it easy to further refine both superclass and subclass (StretchableShape and TshirtSleeve)
• Example: TshirtSleeve inherits StrechableShapeAdditional shape parameters and handlesMethod draw() draws the specific sleeve shape and handles in addition to the generic bounding box (stronger postcondition)
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):ObjectcontainsKey(Object):booleancontainsValue(Object):boolean
MySet
insert(Object)extract(Object):Objectcontains(Object):boolean
Hashtable
put(Object, Object)get(Object):ObjectcontainsKey(Object):booleancontainsValue(Object):boolean
MySet
insert(Object)extract(Object):Objectcontains(Object):boolean
1
1
Implementation inheritance(to be avoided):
Delegation:(to be used instead):
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
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(...…
}
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 (...) {...}
}
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;
}}
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 validIf parameters are off range, print “error”; exit(0);If parameters are off range, throw an exception
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.
Exceptionhandler.
MyExc throw
try { /* normal execution */
} catch (MyExc e) {/* exception handler */
}
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
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”*/
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 84
Example unchecked: invalid formatpublic 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);
}}
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
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 arrayThe handler for IOException does not actually terminate.
• Empty pre-conditionsAssume nothing about the data if something abnormal has occurred
• Meyer’s restaurant class has a “firefighter” exception handlerEnsures the invariant holds when firefighting terminates (post-condition)Assumes nothing when firefighting begins (empty pre-condition)
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 systemSee defensive programming vs. design-by-contract tradeoff
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 = formE.g. many methods with the same name but different signatures
• Resolve the meaning at run-timeThe 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 changeIntroducing a new callee type does not require to change the caller
• More intuitive naming
Cost
• Dynamic linkingExtra overhead for making the method call
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;
}
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
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) {
...
}
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();
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
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 boundariesE.g., [Handout,fig.2]Sometimes, open to interpretation.
• E.g. Spice: Enter schematics, models, initial valuesSimulateOutput waveform
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 commandParse commandInvoke corresponding application routine
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 boundariesIn the case of transaction flow, identify transaction center
• Map DFD into program structureTransform flow:
1st level factoring: input/processing/outputE.g. [Handout,fig.5]
2nd level factoring: map bubbles into modulesStart from centerE.g. [Handout,fig.6]
Transaction flow:Dispatch modules and worker modulesFactor each “action path” using transform mapping
• Refine resulting structureE.g. [Handout,fig.7]; compare to [Handout,fig.8]
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
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 gainE.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 againIf optimum is not obtained, try another choiceE.g. skip largest item at one step in packing algorithm
• Divide et impera: solve subproblems firstE.g. quicksort: sort sublists first
• ...
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
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 pathscost[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 nnumNodes = 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)
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 timeSometimes, that’s good enoughOften, 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
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, 1968Controversial: ““Goto considered harmful” considered harmful”Proven by practice...
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
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
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”;
}
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”?
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 designE.g. C “headers”
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
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 109
User-interface design
UI types
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
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
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
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?
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 expertsFrequent things should be easy, complex things should be possibleE.g. shorthands for frequent actions, customization for complex actionsOffer alternate commands (e.g. pop-up menus)
• Flow of screens should match the flow of workStoryboarding
• Use short, intuitive command labels
• Open windows in the center of action
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 imageSystem perception (user's model): mental model formed by the users about interacting with the systemSystem image: how the system actually appears to the users
• Resemble manual tasksTask analysis
• Minimize memorization, dialogues, motion, etcReduce memory load on usersReduce number and complexity of commandsReduce length and complexity of system specUse 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
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 instantaneousOver 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
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 messagesProblem-orientedConstructive (indicate repair action)Indicate consequencesNon-judgmentalMay contain visual and audible cues
• Undo, redo
• Disable improper commandsContext-dependent
• Allow skipping redundant info (units of measure, .00, etc)
• System response timeDurationPredictability
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