310 likes | 324 Views
Learn about Command, Decorator, State, and Proxy design patterns in software development, including their motivations, advantages, and interactions. Discover how these patterns improve code structure and maintainability.
E N D
LECTURE 18: Design PatternsCommand, Decorator, State, Proxy Ivan Marsic Rutgers University
Topics • Command Pattern • Decorator Pattern • State Pattern • Proxy Pattern • Protection Proxy
Command Pattern Motivation create( params ) doAction( params ) doAction( params ) execute() Client A Server B Client A Command Receiver B unexecute() (Server) (a) (b)
Command Pattern Motivation • Motivation: To separate parameter preparation from passing program control (decision on when to call) Parameters may be prepared by a different object (“Custodian”) create( params ) doAction( params ) doAction( params ) execute() Client A Server B Client A Command Receiver B unexecute() (Server) • 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 Preparation Execution
Client Method 1 Method 2 Server (Receiver) Command Pattern Motivation • Problem:Variable and evolving method signature • If Server code changes, Client code needs to change, too Before:
Client Method 1 Method 2 Method 2 Method 1 Server (Receiver) Command 1 execute() Client execute() Server (Receiver) Command 2 Command Pattern Improvement The change towards the Cmd. pattern may not appear radical when viewed overall, but looking from the client’s standpoint, the simplification is significant. Before: After: The interface to the Server object is much simpler.
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
Command Pattern (b) (a) (c) Forward execution (do)
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 • NOTE: • The Command method execute() does not return a result • Through a Command, the client gives an order and does not expect to be answered back • If the result of a command execution is needed,it should be retrieved separately, after execute() returns
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)
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”)
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 (a) (b) Pre-processing Uniform method calling, regardless of the head-of-the-list object identity (c) Pre- versus Post-processing is defined relative to the essential feature: the request() of RealSubject Post-processing
State diagram for Display Interaction: button-pressed / display msg Not-Worn wearing-detected / measurement-initiated / Ready Measuring measurement-completed / button-pressed / display msg button-pressed / display msg battery-charged / failure-detected / [battery-level threshold] / Faulty Discharged [battery-level threshold] / button-pressed / display msg button-pressed / display msg Example: Midterm #2, Spring 2013 Given: Problem: Design the UML sequence diagram; apply design patterns
client ButtonCheck + increment() FaultyCheck + checkSensor() BatteryCheck + checkBattery() DeviceWearChk + checkWearing() MeasuringCheck + chkMeasuring() BP_Check + checkBP() + display() HR_Check + checkBP() + display() AL_Check + checkAL() + display() BL_Check + checkBL() + display() Concrete Decorators Real Subjects Student solution: Class diagram Subject and Decorator interface «interface» DeviceCheck + check() BP = blood pressure HR = heart rate AL = activity level BL = battery level
client ButtonCheck + increment() FaultyCheck + checkSensor() BatteryCheck + checkBattery() DeviceWearChk + checkWearing() MeasuringCheck + chkMeasuring() BL_Check + checkBL() + display() BP_Check + checkBP() + display() AL_Check + checkAL() + display() HR_Check + checkBP() + display() Real Subjects Concrete Decorators Student solution: Class diagram Subject and Decorator interface «interface» DeviceCheck + check() • 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 nextDevice
display Student sol’n: Sequence diagram client : : DeviceWearChk : FaultCheck : Measuring Check : BPCheck : HRCheck buttonPress( ) checkWearing() checkFault() checkMeasuring() device not worn OR faulty OR measuring alt display error msg [else] checkBP() display checkHR()
display Student sol’n: Sequence diagram client : : DeviceWearChk : FaultCheck : Measuring Check : BPCheck : HRCheck • 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!! buttonPress( ) checkWearing() checkFault() checkMeasuring() device not worn OR faulty OR measuring alt display error msg [else] checkBP() display checkHR()
client Decorator + request() Measurement + request() Another Student Solution • Correct use of the Decorator pattern:All Decorators implement the same interface «interface» Subject + request() next object SafetyZoneChecker + request() ActivityLevelChecker + request()
Another Student Solution • Uniform calling approach —client calls only the head of the “linked list” client : : SafetyZoneChecker : ActivityLevelChecker : Measurement request( args ) request( args ) request( args ) result and ‡ denote added special- case processing result checkActivityLevel( ) result‡ checkSafetyZones( )
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”)
State Pattern (b) (a) (c)
State Pattern (b) (a) (c) (d)
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
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)
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