240 likes | 385 Views
C++ Certificate Program C++ Intermediate. Decorator, Strategy, State Patterns. Overview. Two common design patterns, will provide good insight on inheritance: Decorator and Strategy Additionally, State pattern can be considered a kind of Strategy pattern. Motivation.
E N D
C++ Certificate ProgramC++ Intermediate Decorator, Strategy, State Patterns
Overview • Two common design patterns, will provide good insight on inheritance: Decorator and Strategy • Additionally, State pattern can be considered a kind of Strategy pattern
Motivation • Both provide alternative mechanism for extensibility, sometimes more appropriate than inheritance • Inheritance common use: augment or change the implementation of an object or class responsibility • Unlike inheritance-based (or C++ template-based) approaches, decorators and strategies can change this implementation during run-time
More Motivation • Decorators allow many perspectives of same underlying object to exist, each with potentially varied responsibilities • Sometimes not clear whether to use Decorator or Strategy; intent and implementations are different, however
Decorator and Strategy Intents • From Design Patterns GoF book • Decorator—"Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality." • Strategy—"Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it."
Decorator Pattern • Used to add additional functionality to a particular object • Alternative to a slew of derived classes Classes: Vehicle, VehManTran, VehAutoTran, SedanManTran, SedanAutoTran, VehDiesel, VehGas, etc (where does it stop?)
Decorator Pattern • Prevents inheritance “combinatorial explosion” • Can dynamically extend a single object instance • Can add functionality to a single object and leave others like it unmodified (as opposed to inheritance) • Presents interface identical to object it contains: • To client code, indistinguishable from decorated object • Decorators may be recursively nested
Decorator Details • Forwards all calls to contained object, adds functionality (either before or after method call) • Can be changed at runtime by swapping out with other decorator instances • Should not be visible to the contained object • Compare with inheritance, where change is static, determined at compile time
Implementation Details • As decorator must look, feel, smell like the object it decorates, usually must derive from same tree • Base class should be lightweight; keep decorator classes light enough to use in quantity
Decorator Motivation class Color { public: void invert (); // … }; class Picture { public: virtual Color getPixelColor (long x, long y) = 0; }; // new functionality wanted: inverted Color Picture … // derive a new class?
Decorator Example // picture class interface same as before class PictureImplementation : public Picture { public: virtual Color getPixelColor (long x, long y); }; class ColorInverterDecorator : public Picture { Picture* mComponent; public: virtual getPixelColor(long x, long y) { Color col(mComponent.getPixelColor(x,y); return col.invert(); } };
Decorator Usage Example Picture* orig (new Picture( … )); Picture* inverted (new ColorInverterDecorator(orig)); Picture* brightened = new BrightenerDecorator (inverted, 2); // brighten 2% // alternatively Picture* brightInvertedNewPic = new BrightenerDecorator( new ColorInverterDecorator ( new Picture(…)), 2.0); orig->getPixelColor (1, 1); brightened->getPixelColor (1, 1); // problem to resolve – who delete’s the objects?
Strategy Pattern Motivation • Many related classes differing only in behavior • Sometimes need different variants of algorithm applied against object • Algorithm uses data that client code shouldn't know about • Traditionally, algorithms selected from multiple conditional statements • Ugly and unscalable
Strategy Pattern Description • Decouples an algorithm from its target class • Object and behavior are put in separate classes • Many algorithms for one class, owner class uses many algorithms (strategies) • The STL uses the strategy pattern a great deal (implemented with C++ templates) • Templates are a powerful use of the strategy pattern
Strategy Example class Picture { ConvStrategy* mStrategy; public: Color getPixelColor (long x, long y); setConversionStrategy(ConvStrategy*); }; class ConvStrategy { public: virtual void convert(long x, long y, Color&) = 0; }; Class ColorInvStrategy : public ConvStrategy { Public: virtual void convert(long x, long y, Color&); };
Strategy Advantages • Often easier to manage many algorithms as separate entities • Can change the algorithm at runtime (using inheritance and runtime polymorphism) • Adding new algorithm does not involve modifying the owner class • With inheritance, derived class behavior changes are static; strategies swapped at runtime are dynamic • Removes need for conditional statements code to select algorithms
Disadvantages • Clients must be aware of different strategies in order to select appropriate one • Potentially exposes implementation details • Increased number of objects
State Pattern Motivation • Objects have "state" describing exact conditions at a given time based on attribute values (review: objects have state and behavior) • Object’s state may affect its behavior (decisions made at runtime dependent on attribute values) • Would like to avoid hard-coding this logic
State Pattern • Like Strategy, can use composition as means of delegating roles of a state • Unlike Strategy, client doesn’t care about the strategy - “strategy” selected based upon logic and values internal to the class
State Pattern • In effect implements “dynamic reclassification” • Encapsulates the behavior of an object’s state in a separate class • State design pattern is a fully encapsulated, self-modifying Strategy design pattern • Example: Assume following state transitions: OFF -> (turn ignition on) -> IDLE IDLE -> (engage gear) -> MOVING MOVING -> (set gear to park) -> IDLE IDLE -> (turn ignition off) -> OFF
State Example class Vehicle { public: // various ctors void turnOn(); // { mState->turnOn(*this); } void engageGear(); // { mState->engageGear (*this); } void disengageGear(); //{ mState->disengageGear (*this); } void turnOff(); //{ mState->turnOff (*this); } private: friend class VehState; void changeState (VehState* newState); private: VehState* mState; };
State Example class VehState { public: virtual void turnOn(Vehicle&); virtual void engageGear(Vehicle&); virtual void disengageGear(Vehicle&); virtual void turnOff(Vehicle&); protected: void changeState (Vehicle&, VehState* newState); }; class MovingState : public VehState { public: // could be implemented as Singleton virtual void disengageGear(Vehicle& veh); }; class IdleState : public VehState {… };
State Pattern Use • Use when object behavior defined by state, and behavior changes at runtime depending on state • Operations have large, multipart conditional expressions that depend on object state • State typically represented by one or more enumerated constants • Several operations will typically have division of logic branched by same conditional logic • State pattern puts each conditional branch in a separate class
Pros and Cons • Localizes state-specific behavior and partitions behavior for different states • Makes state transitions explicit • Obvious when moving from one state to another: another state class instance must be swapped in • State objects can be shared • Does not specify logic for transitioning from one state to another