11-Design
11-Design
Design Overview
Software design is an iterative process through which
requirements are translated into a “blueprint” for
constructing the software.
▪ Initially, the blueprint depicts a holistic view of software.
That is, the design is represented at a high level of
abstraction
▪ Directly traced to the specific system objective and
more detailed data, functional, and behavioral
requirements
▪ During the design process refinements lead to much
lower levels of abstraction
Design Overview
Requirement/User Story
As a student, I want to search for courses through a search
page.
▪ What are the components/objects?
▪ Search Page
▪ Courses
▪ Do we need the student as an object?
▪ What questions could we ask to further refine?
Design Refinement
Requirement/User Story
As a student, I want to search for courses
through a search page.
▪ New Components: Button, Input Field,
Results Page
▪ Connections: to the new components
▪ Does the University already have a course
database to connect to?
▪ Components/Modules – turns into
collections of functions, classes, or other
components. These pieces represent
simple problems that the developers
individually implement.
Module/Design
Sequence Diagram
Design: Object Interactions
System Sequence Diagram
User
«initiating actor»
ystem
: System
Timer
«offstage actor»
checkKey()
sk := getNext()
select function(“unlock")
start ("duration“)
start ("duration“)
System Sequence Diagrams
▪ Actors and System
Components – at top Use case: Unlock
▪ Lifelines – under actors and
system
▪ Activation – rectangles
▪ Actions – extend from
lifelines; usually with a
message describing the
action
▪ Solid Line – message symbol
▪ Dashed Line – return symbol
System Sequence Diagrams – More Complex
▪ Alt – if/then like behavior
▪ Loop – for/while loop behavior
System Sequence Diagram Example:
SafeHome Use Case Unlock
Alternate scenario (burglary attempt)
Main success scenario
: System
User LockDevice LightSwitch Timer
: System
«initiating actor» «supporting actor» «supporting actor» «offstage actor» User AlarmBell Police
select function(“unlock") «initiating actor» «supporting actor» «offstage actor»
select function(“unlock")
prompt for the key
loop prompt for the key
enter key
verify key
enter key
verify key
Symbolizes Symbolizes
“worker”-type “thing”-type
concept. concept.
«entity» «entity»
KeyChecker KeyStorage
«boundary» «entity»
KeycodeEntry Key
Mapping Q: who performs the verification? Based on what data? return value
Actions to Key Checker, based on entered key-code and stored valid keys message: ???
Key Checker, based on entered key-code and stored valid keys message: ???
versus
❑ Which is better?
❑ How do we evaluate the “goodness” of design?
How Data Travels:
Safe Home Example
Option A:
“expert” (Key Checker) passes the information (key validity) to another object (Controller)
which uses it to perform some work (activate the lock device)
Which is better?
“expert” on key validity
Option B:
Advantage: “expert” (Key Checker) directly uses the information (key validity)
Shorter communication chain to perform some work (activate the lock device)
Option A: Option B:
“expert” (Key Checker) passes the information (key validity) to “expert” (Key Checker) directly uses the information (key validity)
another object (Controller) which uses it to perform some work to perform some work (activate the lock device)
(activate the lock device)
dl := isDaylight()
[else] numOfAttempts++
activate( "alarm" )
[else]
enterKey()
«create»
: Controller k : Key : Checker : KeyStorage : LockCtrl
loop [for all stored keys]
val := checkKey( k )
k := create() sk := getNext()
compare(k, sk)
checkKey(k) loop
sk := getNext()
logTransaction( k, val )
«destroy»
setValid(ok)
alt val == true activate( "lock" )
controlLock(k)
ok := isValid() dl := isDaylight()
opt ok == true
opt dl == false activate( "bulb" )
setOpen(true)
compare(k, sk)
logTransaction( k, val )
controlLight() «destroy»
dl := isDaylight()
alt val == true activate( "lock" )
▪ AlarmCtrl will determine the list of who needs to be prompt: "try again"
Variation 3 checkIfDaylightAndIfNotThenSetLit()
controlLight() dl := isDaylight()
dl := isDaylight()
opt dl == false
opt dl == false
setLit(true)
setLit(true)
This solution need
separate methods to:
- Turn light ON
- Turn light OFF
❑ It may seem helpful that checkIfDaylightAndIfNotThenSetLit() is named informatively (reveals the intention) …
❑ but the low-level knowledge of operating a particular device (lighting) is encoded in the name of the method …
❑ which, in turn, means that low-level knowledge (“mechanism”) is imparted onto the caller (Controller) which
should be concerned with high-level business policies
❑ Mixing low-level knowledge with high-level knowledge results in rigid and non-reusable designs
Are we done with UC-1: Unlock?
❑Didn’t check that the user is at the right door
▪ Missing: Managing access rights
❑Didn’t distinguish critical and non-critical functions
▪ For example, what if logTransaction() call to Logger does not return, e.g., no access
to database (network outage) or disk-space full ?
▪ Missing: Independent execution of non-critical functions
❑Adding new household devices causes major design changes
❑Controller has several unrelated reasons for future changes:
▪ Business policies are entangled with authentication mechanisms
▪ Device management
❑Etc.
policy:
Controller
# numOfAttemps_ : long
# maxNumOfAttempts_ : long
+ enterKey(k : Key)
– denyMoreAttempts() KeyChecker
1
DeviceCtrl
1 logger
# devStatuses_ : Vector
Logger + activate(dev : string) : boolean
+ deactivate(dev :string) : boolean
+ logTransaction(k : Key) + getStatus(dev : string) : Object
Principles and Patterns
❑Principles are used to diagnose problems with
designs
❑Patterns are used to address the problems
Some Software Design Goals
Modular
▪ Software is logically partitioned into elements or subsystems
Independence
▪ Design should lead to components that exhibit independent functional
characteristics
Complexity Reduction
▪ Interfaces between components and the external environment should be as
simple as possible
Motivated by Requirements
▪ Design should be driven by information obtained during requirement analysis
Easy to Understand
▪ Design should be effectively communicated
SOLID Design Principles for OO
Software inevitably changes/evolves over time (maintenance, upgrade)
❑ Single responsibility principle (SRP)
▪ Every class should have only one reason to be changed
▪ If class "A" has two responsibilities, create new classes "B" and "C" to handle each responsibility
in isolation, and then compose "A" out of "B" and "C"
❑ Open/closed principle (OCP)
▪ Every class should be open for extension, but closed for modification
▪ Put the system parts that are likely to change into implementations (i.e. concrete classes) and define parts that are unlikely to change (e.g.
abstract base classes)
▪ A class should be extendable with modifying the class itself.
❑ Liskov substitution principle (LSP)
▪ objects of a superclass should be replaceable with objects of its subclasses without breaking the application (requirements determined by its
clients!)
▪ When you create derived classes, be sure they conform to pre- and postconditions
❑ Interface segregation principle (ISP)
▪ Keep interfaces (between classes) as small as possible, to avoid unnecessary dependencies
▪ Ideally, it should be possible to understand any part of the code in isolation, without needing to look up the rest of the system code
❑ Dependency inversion principle (DIP)
▪ Loosely coupling software modules in a way that high level modules should not depend on low level modules
▪ Instead of having concrete implementations communicate directly (and depend on each other), decouple them by formalizing their
communication as an abstract interface based on the needs of the higher-level class
SRP Example
Library System – Book Class
Book
Thoughts?
# author : Person
# datePublished: Date
- title : String We can easily think of two different actors here:
….
▪ Book Management (like the librarian)
+ getTitle : String
+ getAuthor: Person ▪ Data Presentation Mechanism (graphical UI, text-only
+ turnPage
+ printCurrentPage UI, maybe printing).
….
You should consider a class as being closed to editing once it has been
• tested to be functioning properly
• all the attributes and behaviors are encapsulated
• proved to be stable within your system
Which is correct?
Subtype requirement
Let object x be of type T1 and object y be of type T2. Further,
let T2 be a subtype of T1.
Any provable property about objects of type T1 (supertype)
should be true for objects of type T2 (subtype).
Rectangle
+ width : double
+ height : double
Is the subtype requirement
+ getArea : double fulfilled?
….
LSP Example (continued) Rectangle
+ width : double
+ height : double
Subtype requirement + getArea : double
Let object x be of type T1 and object y be of type T2. Further, ….
(mechanism/service/utility):
▪ High-level module defines its desired
interface for the low-level service (i.e., high-
level depends on itself-defined interface) Key Descriptor Key Validator Valid Keys Storage
depends-on «interface»
client : PolicyClass («uses») PolicyServiceInterface
developer B
DIP Example: Smart Home
Scenario: replace numeric-
PhotoSObsrv Key KeyStorage
code based keys with – code_ : string
+ isDaylight() : boolean + getNext() : Key
magnetic card or RFID chip k
– timestamp_ : long
– doorLocation_ : string
sensor validKeys
▪ What part of the system
needs to be replaced? Controller
# numOfAttemps_ : long
▪ What is the “interface” # maxNumOfAttempts_ : long
DeviceCtrl
reader
# devStatuses_ : Vector
KeyReader + activate(dev : string) : boolean
+ deactivate(dev :string) : boolean
+ acquireKey() : Key + getStatus(dev : string) : Object
DIP Summary
Dependency Inversion
❑ Change the referencing of concrete classes from being direct to indirect
❑ Generalization the behaviors of your concrete classes into abstract
classes and interfaces
❑ Client classes interact with your system through a generalization rather
than directly with concrete resources
❑ Put an emphasis on high level dependency over low level concrete
dependency