1 ivan marsic rutgers university lecture 18: design patterns command, decorator, state, proxy
TRANSCRIPT
3
Command Pattern Motivation
ClientA
ClientA
ServerB
ServerB
doAction( params )
(a)
ClientA
ClientA
execute()
ReceiverB
ReceiverB
doAction( params )
CommandCommand
create( params )
(b)
unexecute()(Server)
4
Command Pattern Motivation
Motivation: To separate parameter preparation from passing program control (decision on when to call)
ClientA
ClientA
ServerB
ServerB
doAction( params )
ClientA
ClientA
execute()
ReceiverB
ReceiverB
doAction( params )
CommandCommand
create( params )
unexecute()
Reasons for separation:– Separate preparation of calling parameters (which may become available
much before the execution time or may become available incrementally)• All calling parameters become localized in a Command object (“encapsulated”)• Parameters may be prepared for the Client by a different object (“Custodian”)• Client and Custodian objects’ codes may evolve separately
– I.e., different developers develop and maintain or upgrade these classes
– May prepare all Commands in a list (with different parameters or different Receivers) and simply iterate through the list to execute all
– For un-execute (roll-back) capability
Parameters may be prepared by a different object (“Custodian”)
(Server)Preparation Execution
5
Command Pattern Motivation
Problem:Variable and evolving method signature– If Server code changes, Client code needs to change, too
Client
Server (Receiver)
Before:
6
Command Pattern Improvement
Client
Server (Receiver)
Before:
After:
The interface to the Server object is much simpler.
Client
Command 1
Command 2
Server (Receiver)
The change towards the Cmd. pattern may not appear radical when viewed overall, but looking from the client’s standpoint, the simplification is significant.
7
Command Pattern Improvement
Command provides a uniform method signature (“interface”) to the Server– The Command interface never changes, so Server
changes do not force Client changes
Client only decides when to execute()
Client evolution is decoupled from Server implementation and Command implementation– Client versus Server/Command can be responsibilities
of different developers
8
Command Pattern
(a)
Forwardexecution(do)
(c)
Command
Knowing Responsibilities:•Knows receiver of action request•Optional: May know whether action is reversible
Doing Responsibilities:•Executes an action•Optional: May undo an action if it is reversible
custodian client cmd : Command
execute()
receiver
doAction(args)
create(receiver, args)
accept(cmd)
: CommandHistory
log(cmd)
Executed ata different time
Dependency injection
clientclient «interface»Command
+ execute()
ActionType1Cmd
+ execute()
ActionType2Cmd
+ execute()
Receiver1
+ doAction1()
Receiver2
+ doAction2()
receiver
receiver
“Servers”(b)
9
Command Advantages
Execution is usually called with other business logic– Now it is decoupled from parameter preparation,
which can be done at another place (“staging area”), not interfering with business logic (“execution area”)
Business logic is decoupled from parameter preparation
• Client and Custodian codes may evolve independently, by different developers
10
Command Pattern Interaction
Many times commands can be reversed/undone Extended interface to check for reversibility and, if
true, undo
(a)
(b)
Reverse execution(undo)
opt
client : Command
undo()
receiver
isReversible()
: CommandHistory
reversible == trueunexecute()
doInverseAction()
setCurrentCmdPtr()
ActionType1Cmd
+ execute()+ unexecute()+ isReversible()
ActionType2Cmd
+ execute()+ unexecute()+ isReversible()
«interface»Command
+ execute()+ unexecute()+ isReversible() : boolean
Receiver
+ doAction()+ doInverseAction()
receiver
ActionType1Cmd
+ execute()+ unexecute()+ isReversible()
ActionType2Cmd
+ execute()+ unexecute()+ isReversible()
«interface»Command
+ execute()+ unexecute()+ isReversible() : boolean
Receiver
+ doAction()+ doInverseAction()
receiver
11
Decorator Pattern Motivation
Motivation: To separate essential from non-essential functions and allow easy adding of new non-essential functions– Implies only one Subject — one essential function, one
“responsibility” Solution: Client only has a pointer to the
“head of the list” (of services) and does not know the true identity of the head object– All services in the list look the same since all implement the same
interface (here, “list” is not a list data structure!!) Advantages:
– When a new optional function/service is added, client code does not need to change
– Only need to program the new function/service and insert it in the “linked list” ( “dependency injection”)
12
Decorator
Client only knows the head-of-the-list– But doesn’t know it’s identity (RealSubject vs. Decorator),
because all list elements implement the same abstract interface (Subject)
– Client doesn’t know how many Decorators are in the list The list can seamlessly expand or shrink
(a)(b)
Decorator
Knowing Responsibilities:•Knows next decorator or real subject•Has same interf ace as real subject
Doing Responsibilities:•Contributes a special-case processing•Forwards the request to next objectin chain (decorator or real subject)
clientclient «interface»Subject
+ request()
RealSubject
+ request()
RealSubject
+ request()
Decorator
+ request()
Decorator
+ request()
next object
ConcreteDecorator1
+ request()
ConcreteDecorator2
+ request()
13
: ConcreteDecorator2 : RealSubject
addedProcessing( )
result
moreAddedProcessing( )
: ConcreteDecorator1client :
addedProcessing( )request( args )
moreAddedProcessing( )result‡
request( args )
request( args‡ )
result‡
and ‡ denote added special-case processing
Decorator
(a)
(c)
(b)
Decorator
Knowing Responsibilities:•Knows next decorator or real subject•Has same interf ace as real subject
Doing Responsibilities:•Contributes a special-case processing•Forwards the request to next objectin chain (decorator or real subject)
clientclient «interface»Subject
+ request()
RealSubject
+ request()
RealSubject
+ request()
Decorator
+ request()
Decorator
+ request()
next object
ConcreteDecorator1
+ request()
ConcreteDecorator2
+ request()
Uniform method calling, regardless of the head-of-the-list object identity
Pre-processing
Post-processing
Pre- versus Post-processing is defined relative to the essential feature:the request() of RealSubject
14
Decorator Example – GUI Options
Device PreferencesFile Configure Help
CloseApply
Activate for burglary attemptActivate for burglary attempt
Alarm bellAlarm bell
PolicePolice ……
Activate for valid keyActivate for valid key
LightsLights
MusicMusic
AirAir--conditioningconditioning
HeatingHeating
Send SMSSend SMS
15
Decorator - Example
ControllerController
«interface»DeviceCtrl
+ activate()
LockCtrl
+ activate()– disarmLock()
MusicCtrl
+ activate()– turnOnMusicPlayer()
nextDevice
LightCtrl
+ activate()– turnOnLight()
AlarmCtrl
+ activate()– ...
Subject andDecorator interface
client
RealSubject
Concrete Decorators
16
Deco Example – Unlock Use Case
opt
activate()
: Controller
dl := isDaylight()
alt
[else]
enterKey( )
val == true
numOfAttempts++
alt numOfAttempts == maxNumOfAttempts
activate()
denyMoreAttempts()
[else]
ref val := check the key validity
(see sequence fragment in Figure 2-20)
: LockCtrl: MusicCtrl : LightCtrl : AlarmCtrl: PhotoObsrv
activate()
turnOnMusicPlayer()
activate()
turnOnLight()dl == false
disarmLock()
…
AlarmCtrlpreceded bysuitable decorators
17
Example: Midterm #2, Spring 2013
State diagram for Display Interaction:
Ready
Faulty
measurement-initiated /
failure-detected /
Measuring
[battery-level threshold] /
Discharged
measurement-completed /
battery-charged /
button-pressed / display msg
[battery-level threshold] /
button-pressed / display msg button-pressed / display msg
button-pressed / display msg
button-pressed / display msg Not-Worn
wearing-detected /
Problem: Design the UML sequence diagram; apply design patterns
Given:
18
Student solution: Class diagram
client «interface»DeviceCheck
+ check()
ButtonCheck
+ increment()
Subject andDecorator interface
BatteryCheck
+ checkBattery()
DeviceWearChk
+ checkWearing()
FaultyCheck
+ checkSensor()
Concrete Decorators
MeasuringCheck
+ chkMeasuring()
BP_Check
+ checkBP()+ display()
HR_Check
+ checkBP()+ display()
AL_Check
+ checkAL()+ display()
BL_Check
+ checkBL()+ display()
Real Subjects
BP = blood pressureHR = heart rateAL = activity levelBL = battery level
19
Student solution: Class diagram
What went wrong?– Decorators and Subjects
don’t implement the top-level interface
• Methods are named differently
– More than 1 Subject• Not in the spirit of this pattern
client «interface»DeviceCheck
+ check()
ButtonCheck
+ increment()
Subject andDecorator interface
BatteryCheck
+ checkBattery()
DeviceWearChk
+ checkWearing()
nextDevice
FaultyCheck
+ checkSensor()
Concrete Decorators
MeasuringCheck
+ chkMeasuring()
BP_Check
+ checkBP()+ display()
HR_Check
+ checkBP()+ display()
AL_Check
+ checkAL()+ display()
BL_Check
+ checkBL()+ display()
Real Subjects
20
display
Student sol’n: Sequence diagram
: DeviceWearChk : FaultCheck : Measuring Check
checkFault()
client :
display error msg
alt
[else]
buttonPress( )
device not worn OR faulty OR measuring
checkWearing()checkMeasuring()
: BPCheck
checkBP()
: HRCheck
checkHR()
display
21
display
Student sol’n: Sequence diagram
What went wrong?– Decorator objects are not forming a “linked list” and calling each other
uniformly — instead, the Client is calling all decorators in sequence!!
: DeviceWearChk : FaultCheck : Measuring Check
checkFault()
client :
display error msg
alt
[else]
buttonPress( )
device not worn OR faulty OR measuring
checkWearing()checkMeasuring()
: BPCheck
checkBP()
: HRCheck
checkHR()
display
22
Another Student Solution
Correct use of the Decorator pattern:All Decorators implement the same interface
client «interface»Subject
+ request()
Measurement
+ request()
Decorator
+ request()
next object
SafetyZoneChecker
+ request()
ActivityLevelChecker
+ request()
23
Another Student Solution
Uniform calling approach —client calls only the head of the “linked list”
: ActivityLevelChecker : Measurement
result
checkActivityLevel( )
: SafetyZoneCheckerclient :
request( args )
checkSafetyZones( )result‡
request( args )
request( args )
result
and ‡ denote added special-case processing
24
State Pattern Motivation
Motivation: To separate state-dependent event-handling functions from each other and allow easy adding of new states and events
Solution: Event-handling object (“server” or “context”) externalizes its state-dependent functionality into different “state objects”– Context only has a reference to the current state object and does not
know its true identity (knows that it’s a state, but not which one)– All State objects look the same (all implement the same interface)
Advantages:– When a new state or event is added, client code does not need to change– Instead of implementing a single big state-transition table, say 10 states by
15 events, we implement 10 State objects• Each State object maintains only a small part of the big table relevant to it
(input-event / next-state)– Only need to program the new State object and link it with other states
according to the transition diagram ( “dependency injection”)
25
State Pattern
(a)
(b)
(c)
State
Knowing Responsibilities:•Knows one set of values (state) ofattributes of the Context object
Doing Responsibilities:•I mplement behavior associatedwith this state of the Context
Context
+ request(evt : Event)
Context
+ request(evt : Event)
«interface»State
+ handle(e : Event)
ConcreteStateA
+ handle(e : Event)
ConcreteStateB
+ handle(e : Event)
currentState
event-2 [condition] / action-2event-1
State-A State-B
event-2 [condition]
event-2 [condition] / action-2event-1
State-A State-B
event-2 [condition]
26
State Pattern
(a)
(b)
(c) (d)
State
Knowing Responsibilities:•Knows one set of values (state) ofattributes of the Context object
Doing Responsibilities:•I mplement behavior associatedwith this state of the Context
Context
+ request(evt : Event)
Context
+ request(evt : Event)
«interface»State
+ handle(e : Event)
ConcreteStateA
+ handle(e : Event)
ConcreteStateB
+ handle(e : Event)
currentState
optopt
: Context
request( event-1 )
currentState : ConcreteStateA
handle( event-1 )
result, nextState
result currentState := nextState
request( event-2 )handle( event-2 )
perform action-2
result, nextState
currentState := nextState
nextState :=ConcreteStateB
condition == true
process event-1
nextState := this
event-2 [condition] / action-2event-1
State-A State-B
event-2 [condition]
event-2 [condition] / action-2event-1
State-A State-B
event-2 [condition]
27
State Pattern
All State objects are instantiated and mutually interlinked with each other (“dependency injection”)– A State object knows the next states (depending on input events
and guard conditions)– State’s method handle(Event) returns the next state
The Context object knows only about one State object (representing the “current state”) and keeps updating it as told by the return value from currentState.handle(Event) .
Context does not know (nor need to know!) the true identity of the current state object – all State objects implement the same abstract interface
28
Proxy
Notes: Proxy is structurally
the same as Decorator, but has different intention
We could have a “linked list” of Proxies, like with Decorators
(a)(b)
(c)
Proxy
Knowing Responsibilities:•Knows the real subject of requests•Has same interf ace as real subject
Doing Responsibilities:•I ntercepts & preprocesses requests•Ensures safe, effi cient & correctaccess to the real subject
clientclient «interface»Subject
+ request()
RealSubject
+ request()
RealSubject
+ request()
Proxy
+ request()
Proxy
+ request()
realSubject
client : : Proxy
result?
: RealSubject
preprocessRequest( )
postprocessResult( )
request( args )
request( args )
result
opt constraint satisfied
denotes possiblypreprocessedinput arguments
29
Protection Proxy – Example (1)
What if we wanted to add a Maintenance role & access privileges?– Instead of hard-coding the new role privileges,
we just define a new Protection Proxy
[ user == sys-admin ]
[ user == landlord ]
[else]
Grant full accessto metadata and data
Grant read/write accessto all data
Grant read-only access to personal data and activity data for own apartment
Deny all access
[else]
[ user == tenant ]
[else]
Obtain user roleand credentials
30
Protection Proxy – Example (2)
client : Controllerclient : Controller
dBase
«interface»java.sql.Connection
+ createStatement( … ) : Statement+ getMetaData() : DatabaseMetaData…
«interface»java.sql.Connection
+ createStatement( … ) : Statement+ getMetaData() : DatabaseMetaData…
request( ) methods
Subject
ConnectionImpl
…
+ createStatement( … ) : Statement+ getMetaData() : DatabaseMetaData…
ConnectionImpl
…
+ createStatement( … ) : Statement+ getMetaData() : DatabaseMetaData…
RealSubjectRealSubject
dBc dBcDBConTenant
# credentials_ : Object
+ createStatement( … ) : Statement+ getMetaData() : DatabaseMetaData…– checkRequestAuthorized()– createStatmProxy( … ) : Statement
DBConTenant
# credentials_ : Object
+ createStatement( … ) : Statement+ getMetaData() : DatabaseMetaData…– checkRequestAuthorized()– createStatmProxy( … ) : Statement
tenant’s Proxy
DBConAdmin
# credentials_ : Object
+ createStatement( … ) : Statement+ getMetaData() : DatabaseMetaData…– checkRequestAuthorized()– createStatmProxy( … ) : Statement
DBConAdmin
# credentials_ : Object
+ createStatement( … ) : Statement+ getMetaData() : DatabaseMetaData…– checkRequestAuthorized()– createStatmProxy( … ) : Statement
admin’s Proxy
Factory
+ getDbaseConnection(credentials : Object) : java.sql.Connection
Factory
+ getDbaseConnection(credentials : Object) : java.sql.Connection
factory
Factory patternfor creating Connectionand wrapping with Proxy
31
Protection Proxy – Example (3)
dBase := getDbaseConnection( credentials )
: Controller factory : Factory
[credentls == "landlord"]
alt
proxyLL :DBConLlord
dBc :ConnectionImpl
proxyTN :DBConTenant
[credentls == "admin"]
return proxyAD
return proxyLL
return proxyTN
[else]
[credentls == "tenant"]
return NULL
return SQL Statement Proxy
(a)
(b)
proxyAD :DBConAdmin
dBc := java.sql.DriverManager.getConnection(…)
proxyLL := create( dBc )
proxyTN := create( dBc )
proxyAD := create( dBc )
query := createStatement( … )statm := createStatement( … )
createStatmProxy( statm, … )