Intro to Software Engineering - Module Design

118
Module design McGill ECSE 321 Intro to Software Engineering Radu Negulescu Fall 2003

description

 

Transcript of Intro to Software Engineering - Module Design

Page 1: Intro to Software Engineering - Module Design

Module design

McGill ECSE 321Intro to Software Engineering

Radu Negulescu

Fall 2003

Page 2: Intro to Software Engineering - Module Design

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

Page 3: Intro to Software Engineering - Module Design

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

Page 4: Intro to Software Engineering - Module Design

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

Page 5: Intro to Software Engineering - Module Design

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

Page 6: Intro to Software Engineering - Module Design

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

Page 7: Intro to Software Engineering - Module Design

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

Page 8: Intro to Software Engineering - Module Design

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

Page 9: Intro to Software Engineering - Module Design

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

Page 10: Intro to Software Engineering - Module Design

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

Page 11: Intro to Software Engineering - Module Design

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

Page 12: Intro to Software Engineering - Module Design

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);

}}

}

Page 13: Intro to Software Engineering - Module Design

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

Page 14: Intro to Software Engineering - Module Design

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

Page 15: Intro to Software Engineering - Module Design

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

Page 16: Intro to Software Engineering - Module Design

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()

Page 17: Intro to Software Engineering - Module Design

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

Page 18: Intro to Software Engineering - Module Design

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

Page 19: Intro to Software Engineering - Module Design

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

• …

Page 20: Intro to Software Engineering - Module Design

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*

Page 21: Intro to Software Engineering - Module Design

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

Page 22: Intro to Software Engineering - Module Design

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

Page 23: Intro to Software Engineering - Module Design

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();

Page 24: Intro to Software Engineering - Module Design

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

Page 25: Intro to Software Engineering - Module Design

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()

...

Page 26: Intro to Software Engineering - Module Design

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, ...

Page 27: Intro to Software Engineering - Module Design

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()...

Page 28: Intro to Software Engineering - Module Design

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*

Page 29: Intro to Software Engineering - Module Design

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

Page 30: Intro to Software Engineering - Module Design

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

Page 31: Intro to Software Engineering - Module Design

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

Page 32: Intro to Software Engineering - Module Design

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

Page 33: Intro to Software Engineering - Module Design

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

Page 34: Intro to Software Engineering - Module Design

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

Page 35: Intro to Software Engineering - Module Design

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

Page 36: Intro to Software Engineering - Module Design

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”

Page 37: Intro to Software Engineering - Module Design

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

Page 38: Intro to Software Engineering - Module Design

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

Page 39: Intro to Software Engineering - Module Design

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

Page 40: Intro to Software Engineering - Module Design

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?

Page 41: Intro to Software Engineering - Module Design

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

Page 42: Intro to Software Engineering - Module Design

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

Page 43: Intro to Software Engineering - Module Design

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

Page 44: Intro to Software Engineering - Module Design

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 */

Page 45: Intro to Software Engineering - Module Design

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

)

Page 46: Intro to Software Engineering - Module Design

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)

Page 47: Intro to Software Engineering - Module Design

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

• …

Page 48: Intro to Software Engineering - Module Design

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);

}

Page 49: Intro to Software Engineering - Module Design

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

Page 50: Intro to Software Engineering - Module Design

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)

Page 51: Intro to Software Engineering - Module Design

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.

Page 52: Intro to Software Engineering - Module Design

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;

}}...

Page 53: Intro to Software Engineering - Module Design

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

Page 54: Intro to Software Engineering - Module Design

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)

Page 55: Intro to Software Engineering - Module Design

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 */}

Page 56: Intro to Software Engineering - Module Design

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

Page 57: Intro to Software Engineering - Module Design

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

Page 58: Intro to Software Engineering - Module Design

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;

}...

}

Page 59: Intro to Software Engineering - Module Design

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;

}...

}

Page 60: Intro to Software Engineering - Module Design

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

Page 61: Intro to Software Engineering - Module Design

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;

}}

Page 62: Intro to Software Engineering - Module Design

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

Page 63: Intro to Software Engineering - Module Design

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)

Page 64: Intro to Software Engineering - Module Design

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

Page 65: Intro to Software Engineering - Module Design

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

Page 66: Intro to Software Engineering - Module Design

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

Page 67: Intro to Software Engineering - Module Design

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

• …

Page 68: Intro to Software Engineering - Module Design

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

Page 69: Intro to Software Engineering - Module Design

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

Page 70: Intro to Software Engineering - Module Design

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) {......

}

Page 71: Intro to Software Engineering - Module Design

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)

Page 72: Intro to Software Engineering - Module Design

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;

}

Page 73: Intro to Software Engineering - Module Design

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

Page 74: Intro to Software Engineering - Module Design

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)

Page 75: Intro to Software Engineering - Module Design

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):

Page 76: Intro to Software Engineering - Module Design

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

Page 77: Intro to Software Engineering - Module Design

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(...…

}

Page 78: Intro to Software Engineering - Module Design

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 (...) {...}

}

Page 79: Intro to Software Engineering - Module Design

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;

}}

Page 80: Intro to Software Engineering - Module Design

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

Page 81: Intro to Software Engineering - Module Design

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 */

}

Page 82: Intro to Software Engineering - Module Design

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

Page 83: Intro to Software Engineering - Module Design

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”*/

Page 84: Intro to Software Engineering - Module Design

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);

}}

Page 85: Intro to Software Engineering - Module Design

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

Page 86: Intro to Software Engineering - Module Design

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)

Page 87: Intro to Software Engineering - Module Design

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

Page 88: Intro to Software Engineering - Module Design

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

Page 89: Intro to Software Engineering - Module Design

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;

}

Page 90: Intro to Software Engineering - Module Design

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

Page 91: Intro to Software Engineering - Module Design

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) {

...

}

Page 92: Intro to Software Engineering - Module Design

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();

Page 93: Intro to Software Engineering - Module Design

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

Page 94: Intro to Software Engineering - Module Design

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

Page 95: Intro to Software Engineering - Module Design

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

Page 96: Intro to Software Engineering - Module Design

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]

Page 97: Intro to Software Engineering - Module Design

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

Page 98: Intro to Software Engineering - Module Design

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

• ...

Page 99: Intro to Software Engineering - Module Design

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

Page 100: Intro to Software Engineering - Module Design

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)

Page 101: Intro to Software Engineering - Module Design

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

Page 102: Intro to Software Engineering - Module Design

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...

Page 103: Intro to Software Engineering - Module Design

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

Page 104: Intro to Software Engineering - Module Design

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

Page 105: Intro to Software Engineering - Module Design

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”;

}

Page 106: Intro to Software Engineering - Module Design

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”?

Page 107: Intro to Software Engineering - Module Design

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”

Page 108: Intro to Software Engineering - Module Design

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

Page 109: Intro to Software Engineering - Module Design

McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Module design—Slide 109

User-interface design

UI types

Page 110: Intro to Software Engineering - Module Design

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

Page 111: Intro to Software Engineering - Module Design

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

Page 112: Intro to Software Engineering - Module Design

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

Page 113: Intro to Software Engineering - Module Design

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?

Page 114: Intro to Software Engineering - Module Design

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

Page 115: Intro to Software Engineering - Module Design

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

Page 116: Intro to Software Engineering - Module Design

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

Page 117: Intro to Software Engineering - Module Design

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

Page 118: Intro to Software Engineering - Module Design

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