LECTURE 4: Object Oriented Design
Dr. Mostafa Ezzat
ISSR , Computer Science Dept.
2 Topics
Assigning Responsibilities to Objects
Design Principles
Expert Doer
High Cohesion
Low Coupling
Business Policies
Class Diagram
3 System Sequence
Diagrams
We already worked with interaction diagrams: System Sequence Diagrams
: System
User Timer
«initiating actor» «offstage actor»
select function(“unlock")
prompt for the key
enter key
verify key
signal: valid key, lock open
open the lock,
turn on the light
start ("duration“)
System Sequence Diagrams considered interactions between the actors
4 Design: Object Interactions
Design
Sequence Diagram
System Sequence Diagram
Controller : Checker : KeyStorage : LockCtrl
User
«initiating actor»
ystem
: System
Timer
«offstage actor»
checkKey()
sk := getNext()
select function(“unlock")
prompt for the key
alt val != null setOpen(true)
enter key
verify key
signal: valid key, lock open
open the lock,
[else] val == null : setLit(true)
turn on the light
start ("duration“)
• System Sequence Diagrams considered interactions between the actors
• Object Sequence Diagrams consider interactions between the objects
Metaphor for Software Design:
5 “Connecting the Dots”
:InterfacePage :SearchRequest :Controller :PageMaker :DatabaseConn :Archiver :Notifier :InvestigRequest
Resident Database Landlord
We start with objects/concepts from the Domain
Model and modify or introduce new objects, as
needed to make the system function work.
6 Types of Object Responsibilities
Knowing responsibility: Memorizing data or
references, such as data values, data collections, or
references to other objects, represented as a
property
Doing responsibility: Performing computations,
such as data processing, control of physical devices,
etc., represented as a method
Communicating responsibility: Communicating
with other objects, represented as message sending
(method invocation)
How To “Connect the Dots”
Starting Points: Domain Model from UC-1 (domain concepts):
Symbolizes Symbolizes
“worker”-type “thing”-type
Use Case UC-1: Unlock (flow of events): concept.
«entity»
concept.
«entity»
KeyChecker KeyStorage
1. User enters the key code
2. System verifies that the key is valid «boundary»
KeycodeEntry
«entity»
Key
3. System signals the key validity
4. System signals: «boundary» «control»
(a) to LockDevice to disarm the lock StatusDisplay Controller LockDevice
«boundary»
(b) to LightSwitch to turn the light on HouseholdDeviceOperator
Scenario Walkthrough: User LightSwitch
for mapping a Use Case scenario to the Domain Model
Q: who handles this data?
Interface objects and Controller message: checkKey(k)
Q: who performs the verification? Based on what data? return value
Key Checker, based on entered key-code and stored valid keys message: ???
Q: who signals? Based on what data?
Controller and Interface objects, based on key verification; because they are «boundary»
Q: who signals? Based on what data?
Controller or Key checker ???, based on key verification
8 Design: Assigning
Responsibilities
? ? : DatabaseConn : PageMaker : Controller : Checker : DeviceCtrl
checkKey()
accessList := retrieve(params : string)
R1.
interfacePage := activate( "lock" )
render(accessList : string) ?
R2.
(a) (b)
How Data Travels
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)
key-code key-code is-valid activation-params
Controller Checker Controller LockCtrl
“expert” on key validity
Option B:
“expert” (Key Checker) directly uses the information (key validity)
to perform some work (activate the lock device)
Advantage:
key-code key-code activation-params
Shorter communication chain
Controller Checker LockCtrl
Drawback:
Extra responsibility for Checker
“expert” on key validity
10 Characteristics of Good
Designs
Short communication chains between the objects
Balanced workload across the objects
method_1() method_1()
method_2()
…
method_N()
Low degree of connectivity (associations) among the
objects
11 Design Principles
Expert Doer Principle: that who knows should do
the task
High Cohesion Principle: do not take on too many
computation responsibilities
Low Coupling Principle: do not take on too many
communication responsibilities
There are many more …
12 Design: Assigning
Responsibilities
: Controller : Checker : DevCtrl : Controller : Checker : DevCtrl
checkKey() ok := checkKey()
setOpen(true) setOpen(true)
?
(a) (b)
• Although the Checker is the first to acquire the information about the key validity,
we decide to assign the responsibility to notify the LockCtrl to the Controller.
• This is because the Controller would need to know this information anyway—to
inform the user about the outcome of the key validity checking.
• In this way we maintain the Checker focused on its specialty and avoid assigning
too many responsibilities to it.
13 Cohesion
Low cohesion
High cohesion
14 Responsibility-Driven
1. IdentifyDesign
the responsibilities
domain modeling provides a starting point
some will be missed at first and identified in subsequent iterations
2. For each responsibility, identify the alternative
assignments
if the choice appears to be unique then move to the next
responsibility
3. Consider the advantages and tradeoffs of each
alternative by applying the design principles
select what you consider the “optimal” choice
4. Document the process by which you arrived to each
responsibility assignment
15 UC-4: View Access Log
«html»
interfacePage : : Controller : PageMaker : DatabaseConnection
Resident Database
specify get( queryRequest : string )
query accessList := retrieve(params : string)
request retrieve records
result
interfacePage := render(accessList : string)
alt accessList != NULL
page :=
renderList()
[else] page :=
warning()
result «post page»
displayed
16 Example …
Communicating responsibilities identified for the system function “enter key”:
Responsibility Description
Send message to Key Checker to validate the key entered by the
user.
Send message to DeviceCtrl to disarm the lock device.
Send message to DeviceCtrl to switch the light bulb on.
Send message to PhotoObserver to report whether daylight is
sensed.
Send message to DeviceCtrl to sound the alarm bell.
17 Unlocking Sequence
Diagram
: Controller : Checker : KeyStorage : LockCtrl : LightCtrl : Logger
checkKey()
sk := getNext()
alt val != null setOpen(true)
[else] val == null : setLit(true)
logTransaction(val)
sk = stored key; the process either terminates by matching a stored key or
exhausting the key store.
Key is a dynamic object, unlike others which are static contains keycode, name,
other forms of ID, timestamp, door ID, … -- disposed of after checking
18 Unlock Use Case
: Controller k : Key : Checker : KeyStorage : DeviceCtrl : PhotoObsrv : Logger
enterKey()
«create»
loop [for all stored keys]
val := checkKey( k )
sk := getNext()
compare(k, sk)
logTransaction( k, val )
«destroy»
alt val == true activate( "lock" )
dl := isDaylight()
opt dl == false activate( "bulb" )
[else] numOfAttempts++
alt numOfAttempts == maxNumOfAttempts
denyMoreAttempts()
activate( "alarm" )
[else]
prompt: "try again"
19 Unlock Seq. Diag. Variation
1
: Controller k : Key : Checker : KeyStorage : LockCtrl
k := create()
checkKey(k) loop
sk := getNext()
setValid(ok)
controlLock(k)
ok := isValid()
opt ok == true
setOpen(true)
To avoid an impression that the above design is the only one possible!!
Sets a boolean attribute of the Key object: ok = true/false;
Business logic (IF-THEN rule) relocated from Controller to LockCtrl
20 Unlock Seq. Diag.
Variations 2&3
: LightCtrl : PhotoSObs : LightCtrl : PhotoSObs
controlLight() checkIfDaylightAndIfNotThenSetLit()
dl := isDaylight() dl := isDaylight()
opt dl == false The caller opt dl == false
could be
Controller or
setLit(true) KeyChecker setLit(true)
Depends on which solution you consider more elegant;
It is helpful that checkIfDaylightAndIfNotThenSetLit() is named informatively (reveals the
intention), but the knowledge encoded in the name of the method is imparted onto the caller.
21 Summary of Design
enterKey()
Variations
: Controller
c
k : Key : Checker : KeyStorage : DeviceCtrl : PhotoObsrv : Logger
: DeviceCtrl : PhotoSObs
k := create()
loop [for all stored keys]
val := checkKey(k)
sk := getNext()
compare()
activate( "light" )
logTransaction(k, val) dl := isDaylight()
«destroy»
alt val == true activate(“lock”)
opt dl == false
dl := isDaylight()
a opt dl == false activate(“bulb”) setLit(true)
: Controller k : Key [else]
: Checker
numOfTrials++ : KeyStorage : DeviceCtrl
prompt:
"try again"
k := create()
opt numOfTrials == maxNumOfTrials activate(“alarm”)
loop
b
checkKey(k) : DeviceCtrl : PhotoSObs
sk := getNext()
setValid(ok)
checkIfDaylightAndIfNotThenSetLit()
controlLock(k) dl := isDaylight()
ok := isValid()
The caller opt dl == false
opt ok == true
could be
Controller or
setOpen(true) Checker setLit(true)
Are We Done w/ 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
Business rules are interleaved with authentication
and device management, but business rules are
most likely to change
Etc.
23 Business Policies
IF key ValidKeys THEN disarm lock and turn lights on
ELSE
increment failed-attempts-counter
IF failed-attempts-counter equals maximum number allowed
THEN block further attempts and raise alarm
Should be moved into a separate object:
• Make them explicit part of the model
• Confine the future changes in business policies