SOFTWARE ENGINEERING
Lecture 15 Design Patterns 1
MBA Course Notes Dr. ANH DAO NAM
1
Software Engineering
Slides are from Ivan Marsic and Thomas E. Potok, modified by Anh Dao Nam Textbooks:
(cid:1) Bruegge & Dutoit: Object-Oriented Software Engineering: Using UML,
Patterns and Java, Third Edition, Prentice Hall, 2010.
(cid:1) Miles & Hamilton: Learning UML 2.0, O’Reilly Media, 2006.
Some interesting books for the advanced material include:
(cid:1) R. Pressman, Software Engineering - A Practitioner's Approach, 6th ed.,
2005
(cid:1) C. Ghezzi, M. Jazayeri, and D. Mandriolo, Fundamentals of Software
Engineering. Prentice Hall, second ed., 2002
(cid:1) A. Endres and D. Rombach, A Handbook of Software and Systems Engineering. The Fraunhofer IESE Series on Software Engineering, Pearson Education Ltd., 2003.
(cid:1) S. Robertson and J. C. Robertson, Mastering the Requirements Process.
Addison-Wesley Professional, second ed., 2006.
(cid:1) I. Jacobson, G. Booch, and J. Rumbaugh, The Unified Software Development Process. Addison-Wesley Professional, 1999.
(cid:1) K. Beck and C. Andres, Extreme Programming Explained. Addison-Wesley,
2004.
2
Topics
Software Design Patterns – What & Why
Example Pattern: Publisher-Subscriber (a.k.a. Observer)
3
What Developers Do With Software
(besides development)
Understand Maintain (fix bugs) Upgrade (add new features)
4
The Power of Patterns
NIB
CN
MAP
PLE
5
The Power of Patterns
6
The Power of Patterns
APPLE
IBM
CNN
7
The Power of Patterns
8
The Power of Patterns
NIB
CN
MAP
PLE
APPLE
IBM
CNN
9
Software Design Patterns
Design Patterns help anticipate change Change is bad when there are unrelated reasons to change a software module/class (cid:1) Unrelated reasons are usually because of
unrelated responsibilities
Another target is complex conditional logic (If-Then-Else statements, etc.)
10
What May Change & How
What changes in real world are business rules (cid:2) customer requests changes in software Changes in the small—changes in object responsibilities towards other objects (cid:1) Number of responsibilities (cid:1) Data type, or method signature (cid:1) Business rules (cid:1) Conditions for provision/fulfillment of
responsibilities
Sometimes change is regular (follows simple rules), such as new object state defined, or another object needs to be notified about something
11
Object Responsibilities (toward other objects)
Knowing something (memorization of data or object attributes) Doing something on its own (computation programmed in a “method”) (cid:1) Business rules for implementing business
policies and procedures
Calling methods of other objects (communication by sending messages) (cid:1) Calling constructor methods; this is special because the caller must know the appropriate parameters for initialization of the new object.
12
Patterns for Tackling Responsibilities
Delegating “knowing” and associated “doing” responsibilities (State) Delegating “calling” responsibilities (Command, Publisher-Subscriber) Delegating non-essential “doing” responsibilities (when “doing” responsibility incrementally modified) (Decorator)
13
Key Issues
When a pattern is needed/applicable? How to measure if a pattern-based solution is better? When to avoid patterns because may make things worse? (cid:3) All of the above should be answered in terms of object responsibilities before/after a pattern is applied
14
Publisher-Subscriber Pattern
A.k.a. “Observer”
Disassociates unrelated responsibilities
T
F
?
Helps simplify/remove complex conditional logic and allow seamless future adding of new cases
Based on “Indirect Communication”
15
Request- vs. Event-Based Comm.
Request: doSomething( info )
Request: getInfo()
(1) Request: subscribe()
info
Client
Server
Doer
Doer
Info Src
Info Src
(2) event( info )
(a)
(b)
(c)
Direct Communication
Indirect Comm.
16
“Before” == A Scenario Suitable for Applying the Pub-Sub Pattern
Event Detector
DoerType1
DoerType2
…
…
…
detectEvent() …
tellMeWhatToDo() …
tellMeWhatToDo() …
…
…
Doer1.tellMeWhatToDo() Doer2.tellMeWhatToDo() …
…
Responsibilities Doing:
• Detect events
Calling: • •
Tell Doer-1 what to do Tell Doer-2 what to do
unrelated! ⇒ unrelated reasons to change the Event Detector: • When event detection needs to change or extend • When new doer types need to be told what to do
17
“After” == Responsibilities After Applying the Pub-Sub Pattern
Subscriber
…
Publisher subscribers : List …
detectEvent() …
receiveEvent() …
…
subscriber.receive(Event) …
…
Unrelated responsibilities of the Event Detector (now Publisher) are dissociated:
• When event detection needs to change or extend → change Publisher • When new doer types need to be added → add an new Subscriber type
(Subscribers need not be told what to do – they know what to do when a given event occurs!
18
Publisher-Subscriber Pattern
Focused on classifying events (cid:1) The focus in on the “Publisher” object and
the “environment” that it is observing
(cid:1) Example key events in safe home access:
(cid:1) Entered key is valid (cid:1) Entered key is invalid
Instead of making decisions & issuing orders to Doers/Subscribers (cid:1) Focusing on other objects’ work: deciding
when it is appropriate to call each one
19
Publisher-Subscriber Pattern
PUBLISHER
Controller (receives key-code) Key Checker (checks validity, i.e., classifies: valid/invalid) (cid:1) Publishes the classification result to
“subscribers”
SUBSCRIBERS
Lock Control Light Control Alarm Control (cid:1) Do the work based on the event
classification outcome
( Assumed: subscription step first )
20
Pub-Sub Pattern
Publisher
Knowing Responsibilities:
• Knows event source(s) • Knows interested obj’s (subscribers)
Doing Responsibilities:
«interface» Publisher
*
• Registers/Unregisters subscribers • Notifies the subscribers of events
«interface» Subscriber
subscribers
+ receive()
+ subscribe() + unsubscribe()
Subscriber
Knowing Responsibilities:
Type1Publisher
Type2Publisher
Type1Subscriber
+ receive()
+ subscribe() + unsubscribe()
+ subscribe() + unsubscribe()
• Knows event types of interest • Knows publisher(s) Doing Responsibilities:
• Registers/Unregisters with publishers • Processes received event notifications
(a)
(b)
21
From Chapter 2
Unlock Use Case
enterKey()
k := create()
loop
[for all stored keys]
val := checkKey(k)
sk := getNext()
compare()
logTransaction(k, val)
«destroy»
alt
val == true
activate(“lock”)
dl := isDaylight()
dl == false
opt
activate(“bulb”)
numOfAttempts++
[else]
numOfAttempts == maxNumOfAttempts
alt
denyMoreAttempts()
activate(“alarm”)
[else]
prompt: "try again"
22
: Controller k : Key : Checker : KeyStorage : DeviceCtrl : PhotoObsrv : Logger
Refactoring to Publisher-Subscriber
Conditional logic is decided here, at design time, instead of run time
1. Subscribe for appropriate events
Subscriber
Event type
Pub
Sub-1
LockCtrl, LightCtrl
keyIsValid
Sub-n
AlarmCtrl
keyIsInvalid
LightCtrl
itIsDarkInside
. . .
Sub-n
Pub
Sub-1
No need to consider the “appropriateness” of calling the “servers”
2. When event occurs:
Design-time decisions are better than runtime decisions, because they can be easier checked if they “work” (before the product is developed)
(a) Classify: keyIsValid / keyIsInvalid (b) Notify only the subscribers for the detected event class
If a new device is added -- just write a new class; NO modification of the Publisher!
23
Pub-Sub: Unlock Use Case
: KeyStorage : KeyStorage : PhotoSObs : PhotoSObs : AlarmCtrl : AlarmCtrl : Checker : Checker : LightCtrl : LightCtrl : LockCtrl : LockCtrl k : Key k : Key : Controller : Controller : Logger : Logger
enterKey() enterKey() k := create() k := create()
loop loop checkKey(k) checkKey(k) sk := getNext() sk := getNext()
compare() compare()
alt alt valid == true valid == true for all KeyIsValid subscribers for all KeyIsValid subscribers loop loop keyIsValid() keyIsValid()
keyIsValid() keyIsValid()
keyIsValid() keyIsValid()
dl := isDaylight() dl := isDaylight()
dl == false dl == false opt opt
setLit(true) setLit(true)
[else] [else] for all KeyIsInvalid subscribers for all KeyIsInvalid subscribers loop loop keyIsInvalid() keyIsInvalid() numOfAttempts++ numOfAttempts++
keyIsInvalid() keyIsInvalid() prompt: prompt: "try again" "try again" numOfAttempts == maxNumOfAttempts numOfAttempts == maxNumOfAttempts opt opt
soundAlarm() soundAlarm()
24
keyIsInvalid() keyIsInvalid()
From Hub-and-Spokes (Star) to Token Passing (Cascading) Architecture
: Key : KeyStorage
Before:
getNext()
: Checker create()
checkKey()
After:
: Logger
setOpen() : Controller logTransaction()
: LockCtrl
isDaylight()
: Key getNext() : Checker setLit() create() soundAlarm()
(a)
: AlarmCtrl : KeyStorage : PhotoSObs checkKey() : LightCtrl : Controller keyIsValid() keyIsInvalid()
: LockCtrl : AlarmCtrl
Pub-Sub reduced the class coupling:
: Logger : LightCtrl
isDaylight()
25
(b)
: PhotoSObs
Design Patterns: Delegation
initializes the pattern
Custodian
Instantiation of the Design Pattern
Client
asks for service
collection of objects working to provide service
Alternative names for Custodian are Assembler or Initializer
26
Pub-Sub: Initialization
: Controller : Controller
: Checker : Checker
: LockCtrl : LockCtrl
: LightCtrl : LightCtrl
: AlarmCtrl : AlarmCtrl
: Logger : Logger
create() create()
subscribeKeyIsInvalid() subscribeKeyIsInvalid()
subscribeKeyIsValid() subscribeKeyIsValid()
A method call A method call that passes a that passes a reference to the reference to the Checker Checker
subscribeKeyIsValid() subscribeKeyIsValid()
subscribeKeyIsInvalid() subscribeKeyIsInvalid()
subscribeKeyIsValid() subscribeKeyIsValid()
subscribeKeyIsInvalid() subscribeKeyIsInvalid()
27
Practical Issues
1. Do not design for patterns first
(cid:1) Reaching any kind of solution is the
priority; solution optimization should be secondary
2. Refactor the solution to patterns
(cid:1) E.g., to reduce the complexity of the
program’s conditional logic
Uncritical use of patterns may yield worse solutions!
28
Q&A
29

