Software design
McGill ECSE 321Intro to Software Engineering
Radu Negulescu
Fall 2003
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 2
About this module
Software should be designed not just for performance, but also for maintainability! Why?
• Save on maintenance costsPost-release defect removalMinor adjustments
• Save on verification costsHigher pre-release fault detection and removal rate
• Save on development costsLower fault injection rate
• Facilitate teamworkSoftware that is more maintainable is also easier to understand
Design for maintainability requires special techniques
• Message of the textbook’s cover picture?
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 3
Basic concept: modularity
A software system should be split into modules
• To facilitate future changeBy minimizing the scope of change
• To avoid need for future changeBy containing change within isolated modules
• To reduce development time and effortBy simplifying the systemBy postponing design decisions until optimal time, and avoiding some expensive optimizationsBy allowing comprehension and reasoning on modules and interfaces
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 4
Modularity
Two main criteria:
• Cohesion: a measure of tightness of relationships within a module
• Coupling: a measure of tightness of relationships among modules
Tradeoff:
• Strong cohesion: Focused modulesMore granularity
• Loose coupling:Isolated modulesLess granularity (to avoid split dependencies)
• Optimum: around 7+-2 modules at each level of abstractionThis holds for large modules (subsystems) and small modules (individual objects or routines) alikeThis is just an estimate, not an absolute rule
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 5
Cohesion
“Single-minded functionality”: operations in a module should be strongly related
Types of cohesion:
(HIGH)
• Functional: perform one function only
• Communicational: use the same data
• Sequential: an incomplete sequence of causally-related actions
• Procedural: sequence of non-causally-related actions
• Temporal: actions that are performed at the same time
• Logical: decision branches
• Coincidental: no discernable relationship
(LOW)
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 6
Strong cohesion
Heuristics
• Partition the system according to dependency clusters
• Isolate presentation, data management, and processingMVC architectures
• Isolate control from workFocus on control-only or work-onlyHigh-cohesion “control-only” modules
Dispatching eventsStartup and shutdown routines (delegate the individual tasks)
• Isolate main functionality from auxiliary functionalityException throwingGarbage collection
• A complex name usually indicates poor cohesionSplit “getAndSet” routines
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 7
Example of strong cohesion
From [BD]:
Alternative
Decision
Criterion
subtasks
*SubTask
ActionItem
DesignProblem
Task
assesses
solvableBy
resolvedBy
based-on
* * *
implementedBy
DecisionSubsystem
RationaleSubsystem
PlanningSubsystem
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 8
Decoupling
“Aversion to interaction”: different modules should be detached
Types of coupling:
• Simple-data ("normal"): non-structured parameter list
• Data-structure ("stamp"): structured parameter
• Control: select a callee task by a flag parameterLogical cohesion
• External: the program is tied to a particular device or environment => non-portable
• Global-data ("common"): two routines access the same global dataAt least make it read-only
• Pathological ("content"): use internal data of a different moduleE.g. via pointers
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 9
Loose coupling
Heuristics:
• Small interfaces: few parameters
• Adapted interfaces: convenient for the callee
• Flexible interfaces: convenient for many callersOrthogonality
• Visible interfaces: no direct access to protected data
• Avoid pathological coupling: pass parameters through interfacesfloat[][] A;x = det(A);
• Avoid stamp coupling: decompose bundles of parameters into basictypes or very-high-cohesion parameters (e.g. events)
• Avoid external coupling: use IDEs that produce portable code
• Avoid global-data coupling: In object-oriented programs, use “get” and “set” methods to read and modify object attributes
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 10
Example of loose coupling
Adapted from [BD]: change from binary tree to linked list
• Parse tree for a + b + c
add1:Nodeadd2:Node
c:Nodeb:Nodea:Node
add:Nodec:Nodeb:Nodea:Node
Sharing through attributesclass Node {Node left;Node right;String name;
}
Sharing through operationsclass Node {
Enumeration getArguments();String getName();
}
Sharing through attributesclass Node {
Node next;String name;
}
Sharing through operationsclass Node {
Enumeration getArguments();String getName();
}
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 11
Example of bad modularity
/* If 'direction' is 0, input n rowsinto array 'lines';otherwise output n rows from array 'lines'.Pre: 0 <= n <= length(lines).
*/in_out (
int n, /* number of rows, input or output */int direction /* control flag: 0 = in, 1 = out */
) {int i;for (i = 0; i < n; i ++) {
if (direction == 0) {read one input row into lines[i];
} else {write lines[i];
} }
}
/* Read n lines of input.Output the lines in sorted order.Pre: 0 <= n <= length(lines).
*/input_sort_echo(
int n /* number of rows to be sorted */) {
int i, j;in_out(n, 0);for (i = 0; i < n - 1; i ++) {for (j = i + 1; j < n; j ++) {if (lines[j] <= lines[i] alphabetically) {copy string lines[j] into string temp;copy string lines[i] into string lines[j];copy string temp into string lines[i];
}}
}in_out(n, 1);
}
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 12
Improving modularity
Global-data coupling:
• Both routines access array lines
• Fix: the access to array lines and string temp can be made explicit in the routine interfaces
Logical cohesion:
• The same routine (in_out) performs unrelated operations
• Fix: split into an input routine and an output routine
Communicational or temporal cohesion:
• The same routine (input_sort_echo) controls the flow and does the sorting
• Fix: call sorting as a sub-routine
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 13
Example of improved modularity
/* Input n rows to 'lines';Pre: 0 <= n <= length(lines). */
in(int n, /* number of rows to be input */StringArray lines /* storage */
) {int i;for (i = 0; i < n; i ++) {
input lines[i]; /* read one row */}
}
...sort(
int n, /* number of rows to be input */StringArray lines /*
) { ...
...out(
int n, /* number of rows to be input */StringArray lines /*
) {...
/* Read n rows and output them sorted.Pre: n >= 0
*/input_sort_echo(
int n /* number of rows to handle */) {
Declare and allocate StringArray lines;in(n, lines);sort(n, lines);out(n, lines);
}
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 14
Design process
Design: blackbox model full-detail (clearbox, whitebox, glassbox)
• Choose among several alternativesConsider solutions permitted by the analysis model
Heuristics, patternsConstraints, invariants
Optimize design goals tradeoffsSelect and prioritize goals: maintainability, usability, performance, etc.
• Iterate through the designWhy?
Brainstorming and consolidationPrototypes to clear out technical risksSeveral levels of abstractionSeveral viewpoints, design goalsFixing defects
Know where to stopReview criteria
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 15
Overview of software design
System-level
• Using the analysis model as a starting point, take high-level decisions to optimize the selected design goals
Object-level
• “Close the gap” between the architecture model and the deployment platform
Detailed
• Organize and build code in a way that supports change and reasoning
Specialized design
• UI design
• Function-oriented design
• Real-time, etc.
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 16
System-level design
System-level design typically comprises the following activities:
• Identifying design goals and constraints
• Defining the architectureDecomposing the system into subsystemsSelecting system-wide conventions and policies:
PersistencySecurityGlobal control flowBoundary conditions: start-up, shut-down, error handling
• Selecting reusable components, libraries, etc.
• Mapping to hardware
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 17
Object design
The following activities are typically performed at this level:
• Identifying design objects: implementation-specific classes
• Service specification: precisely describe interfaces
• Component selection: find pre-made parts that are suitable for the functionality of the system
• Object model restructuring: optimize maintainabilityReduce multiplicityImplement binary associations as referencesMerge similar classesCollapse trivial classes into attributesSplit complex classes
• Object model optimization: optimize performanceUse different algorithms / data structuresAdd or remove redundant associationsAdd derived attributes“Open up” the architecture
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 18
Design goals
First thing: determine goals and priorities
• Include viewpoints of several stakeholders
• See [BD, sect.6.4.2] for a comprehensive list of software design goals
• Relative importance varies depending on the nature of the application
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 19
Design goals
End-user / customer / sponsor Developer / maintainerscope xlow cost/effort x xlow dev. time x xlow defect rate x xmodifiability xcomprehensibility x xreliability xrobustness xuser-friendliness xdocumentation x xreusability xadaptability xportability xbackward compatibility xperformance xmemory efficiency x
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 20
Performance measures
Running time is not a single, well-defined measure
• Consider all input data and internal non-determinism
• Worst-case: maximum valueE.g. car airbag controller: worst-case is criticalPolling: time = (#sensors) * (time per sensor) + (alarm time)Interrupts: time = (interrupt path length) * (hub delay) + (alarm time)
• Average-case : weighted by operation frequency (operational profile)E.g. text editor: average-case is more important than worst-caseE.g. videophone image compression: both worst-case and average-case are important
• Amortized: mean taken over an operating cycleE.g. the average number of bit flips in an n-bit number
Answer: 2! (Why?)
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 21
Performance measures
Many performance measures
• Latency vs. throughputE.g., an IDE that does background compilation performs more work to improve average latencyE.g., a web service might have a fixed limit on the response time (latency), and optimize the maximum number of simultaneous requests (throughput)
• Memory requirementsData storage: scalability, garbage collection
• Number of expensive operations (function calls, multiplications, etc.)
• Number of accesses to communications, external storage, or I/O resources
E.g. a trip planner might allow increased response time to minimize wireless communication time
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 22
Design tradeoffs
Typical design tradeoffs:
• Rapid development vs. scope
• Performance vs. maintainabilityOrders of growth
• Performance vs. portability
• Backward compatibility vs. comprehensibility
• Cost, delivery time vs. robustness, reusabilityNot to be confused with cost vs. other quality parameters
• Space vs. speedWhy operating systems, IDEs, etc. will fill up hard drives of any sizes?
Not easily traded:
• Quality vs. effortLow defect rate, maintainability, comprehensibility, documentationEarly in the project: non-tradeableLate in the project: tradeable to a small extent
• Delivery time vs. staffingOnly early in the projectBrooks’ law: adding people to a late project only makes it later
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 23
Software architecture
Architectural design should address:
• Subsystem decomposition
• System-wide policiesData organizationControl flowCommunication protocolsError handlingMapping to hardwareSecurity
• NOT development methodology issues, such as object-orientation vs. function-orientation
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 24
Basic definitions
Subsystems
• Parts of a system
• Can be developed and changed independently (more or less)
• Example: UI evolves independently from business logic
Services
• A service provided by a subsystem = a set of operations of that subsystem that share a common purpose
• Examples: download, upload, notification, management, …
Interfaces
• An interface of a subsystem = a black-box view of that subsystemAs seen by an actor or by another subsystem
• API: operations, signatures, and specificationsSignature = parameter types, return typeExample: doGet method of HttpServlet class
���������������������������������������������������������������������������� ���������������� ���������������� ���������������� ���������������� �������������������� ����������������� ����������������� ����������������� ����������������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 25
Defining the architecture
Decomposition into subsystems
• HeuristicsUse variants of Abbott’s lexical rules: nouns, verbsIdentify groups of objects involved in use cases
Encapsulate functionally related classes - Facade patternIsolate scope overlaps among use cases
Create dedicated subsystems for moving data among subsystemsCreate a separate subsystem for the user interface
Encapsulate legacy code - Adaptor pattern
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 26
Layers and partitions
Different ways of decomposing the system:
• Layering: each subsystem provides a level of abstractionClosed architecture: each layer can access services from the layer immediately below it. E.g. ISO OSIOpen architecture: each layer can access services from any layers below it E.g. Motif toolkit for X11
• Partitioning: peer subsystems with as few dependencies as possible
• Not a clear distinction between layering and partitioning
A B C
D E
F G H
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 27
Common architectures
Design the data first, then the control
• During analysis, may assume objects run whenever needed
• During design, the object behavior should be determined so that the object does indeed run whenever needed
Types of data organization schemes:
• Repository
• Model-view-controller (“architecture”, “framework”, “pattern”)
• Client/server
• Peer-to-peer
• Pipe-and-filter / data flow
Types of control flow:
• Centralized
• Event-driven
• Thread-based
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 28
Repository architecture
Subsystems interact by sharing data in a central repository
The repository may also implement control flow
• Serialize concurrent accesses of processing subsystems
• Activate subsystems depending on state of data (“blackboard”)
E.g. DBMS, compilers [BD], various CAD tools
LexicalAnalyzer
SyntacticAnalyzerSemanticAnalyzer
CodeGenerator
SourceLevelDebugger SyntacticEditor
ParseTree SymbolTable
Compiler
Repository
Optimizer
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 29
Repository architecture
Pros:
• Decoupled subsystems; easy to add new ones
Cons:
• Repository may become a bottleneck
• Strong coupling between each subsystem and the repository
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 30
Model-view-controller
A.k.a. MVC
Partition data representation (model), presentation (view), and control
• One of the first design patterns: model state updates are reported to all views
Refined to “observer pattern” (will see later)May use “Listener” objects in Java
• Special case of the repository architecture, if the model is considered to be the data repository
Controller
Model
subscribernotifier
initiator
*
repository1
1
*
View
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 31
Model-view-controller
Pros:
• Allows independent change of model, view, and controller
• Allows subscription at runtime
• Isolates variability, as the views are less stable than the model
Cons:
• The model may become a performance bottleneck
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 32
Client-server
Servers provide services to clients
• E.g. central database
• E.g. communication systemsWeb server; DNSMail server; news server; ...
• Suitable for distributed systems that process large amounts of dataWhy?
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 33
Client-server
Three-tiered architecture:
Browser
WebServer
Servlet
DataBase
JDBC
http request
service
query
SQL query table
result set
string
web page
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 34
Peer-to-peer
Peer-to-peer:
• Generalize client-server systems
• Each subsystem can request and provide servicesE.g. a database that can also notify the application
• More difficult to designMany possible interleavings of messages/service requestsMany possible modes of failure in concurrent/distributed systems
DeadlockUnfairness/starvationLivelock/divergence...?
Process1
Resource1
Resource2
Process2
1:req
5:req 3:req4:ack
2:ack
6:req
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 35
Pipe-and-filter
Filters: processing subsystems
• Executing concurrently
Pipes: associations between filters
• Data transfer
• Synchronization
E.g. UNIX shell [BD]
ps grep sort more
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 36
Centralized control
Two types
• Call-return (procedure-driven): sequential subroutine callsRigid => easy to debug, but locks resources
• Manager: one component is designated to control a concurrentsystem
E.g.: a repository database that can signal changesFlexible => Better real-time response, efficiencyMore difficult to design => interleaving, modes of failure
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 37
Event-driven control
Two types
• Interrupt-drivenInterrupts are serviced immediately (according to priority)Offers execution time bounds, hence good for real-time systems
• BroadcastEvents are broadcast to several listeners, who will handle them when they canBetter modularity, but no time boundsUsually combined with the MVC architecture
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 38
Broadcast events example
Events are key to implementing graphical user interfaces
• The broadcast method of the source may be invoked automatically upon occurrence of an event
Example: ActionEvents are emitted by Button objects
addActionListener(ActionListener)
removeActionListener(ActionListener)
...
ActionListener
actionPerformed(ActionEvent)
MyListener
actionPerformed(ActionEvent e)
ActionEvent
getActionCommand()getSource()
e
* 1
1
*
1 *
Button
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 39
Broadcast events example
Example: Mouse events are emitted by a Component object
• Applets and other subclasses of Component inherit its methods
• Mouse movement events are sent to a different listener (MouseMotionListener)
MouseListener
mouseClicked(MouseEvent)
mousePressed(MouseEvent)
...
MyListenerMouseEvent
Point getPoint()setPoint(Point)
* 1
1
*
1 *
mouseClicked(MouseEvent)
mousePressed(MouseEvent)
...
addMouseListener(MouseListener)
addMouseMotionListener(MouseMotionListener)
…
Component
...
Applet
1 *
MouseMotionListener
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 40
Using events
Events ensure communication between source and listener while effectively decoupling the functionality of source and listener
• None needs to know the details of the other
• The connection can be made and cancelled dynamically
Event queues may be added to further decouple the timing of source and listener
• Source and listener may operate at different rates
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 41
Thread-based control
Thread-based
• Several streams of execution that respond to different users, different stimuli, different events, etc.
• Example: servletsService method
• Issue: mutual exclusionCommunication through shared variables“synchronize”
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 42
Threads example
class MyThread extends Thread {int k = 0; // the data managed by the Threadint m_id; // unique ID for each Thread
public MyThread(int id) {m_id = id;
}
// print and update the "data", i.e., integer k// invoked whenever the system executes the Thread public void run() {
for(;;) { // foreverSystem.out.println("Thread " + m_id + ": " + k);k++;
}}
}
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 43
Thread-based control
• May use queues to further decouple request generation from request handling
*
Stimulus Thread**
Event
Stimulus Thread*
Event queue
Event*1
*1
Stimulus Thread* *
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 44
Using threads
Threads are used for modularity, not performance
• (Unless the OS scheduler is clever enough to map them to separate processors)
• Decouple different user workflowsDifferent usersDifferent applications of same user
• Tracking different actors
• Mapping to different hardware
• Avoid concurrency
• Handle concurrency with mutual exclusion (synchronize)
McGill University ECSE 321 © 2003 Radu Negulescu Introduction to Software Engineering Software design—Slide 45
References
Modularity
• BD 6.3.3
• McConnell 5.3, 5.4
System-level design
• BD 6.3.1-6.3.5, 6.4.1-6.4.5, 6.4.8-6.4.10
Architectural options
• BD 6.4.7