1 ivan marsic rutgers university lecture 18: design patterns command, decorator, state, proxy

31
1 Ivan Marsic Rutgers University LECTURE 18: Design Patterns Command, Decorator, State, Proxy

Upload: zoe-pierce

Post on 30-Dec-2015

221 views

Category:

Documents


1 download

TRANSCRIPT

1

Ivan MarsicRutgers University

LECTURE 18: Design PatternsCommand, Decorator, State, Proxy

2

Topics

Command Pattern Decorator Pattern State Pattern Proxy Pattern

– Protection Proxy

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, … )