Strategy Pattern is one of the 23 Design Patterns elucidated in the most excellent Gamma And Helm book, Design Patterns: Elements of Reusable Object - Oriented Software by Gamma and Helm et al. ISBN 0201633612 , which is cited below:
Intent: Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it. (Also known as Policy)
Design Pattern Classification: Object Behavioral
Use the Strategy pattern when -
- Many related classes differ only in their behavior.
- You need different variants of an algorithm.
- An algorithm uses data that clients shouldn't know about.
- a class defines many behaviors, and these appear as multiple conditional statements in its operations.
- Strategy - declares an interface common to all supported algorithms. Context uses this interface to call the algorithm defined by a Concrete Strategy.
- Concrete Strategy - implements the algorithm using the Strategy interface.
- Context - configured with a Concrete Strategy object, and maintains a reference to a Strategy object. May also define an interface that lets Strategy access its data.
- Strategy and Context interact to implement the chosen algorithm. A context may pass all data required by the algorithm to the strategy when the algorithm is called. Alternatively, the context can pass itself as an argument to Strategy operations. That lets the strategy call back on the context as required.
- A context forwards requests from its clients to its strategy. Clients usually create and pass a ConcreteStrategy object to the context; thereafter, clients interact with the context exclusively. There is often a family of ConcreteStrategy classes for a client to choose from.
Strategy – INTL Example (by Steven Black)
According to Gamma And Helm, the intent of the Strategy pattern is: Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
Not all localizations are alike. For example, a port from the US to the UK or Canada probably needs only minor interface adjustments, along with perhaps local currency support. A port from the US to Germany, on the other hand, requires extensive interface along with, potentially, localizing images and data sources. A port further afield may require changing fonts and most of the other localizable things. How to do this?
The application localization must be configurable, in the field, on the fly.
The user must be able to configure localizations to suit needs
The user should be able to create new localization strategies without much difficulty.
Separate individual localization tasks into atomic processes, then give each process the same interface so as to make them interchangeable. Give the controlling process a clean inteface for loading strategies, and design the process to call each loaded “strategy” in turn.
A modular set of strategies that can be applied as appropriate. In the case of INTL, certain parts of the localization strategies are so common – lookups in STRINGS.DBF for example – that this capability was migrated up the hierarchy to avoid loading redundant capabilities in each strategy.
Separating the localization tasks into separate strategies, and creating a mechanism to iterate through the loaded strategies, allows for much configuration flexibility. The downside is a performance penalty arising from the some inevitable common elements and from an increase in messaging traffic.
The INTL class Strategy branch.
Layered Strategies anyone?
( Attempting to take another stab here at adding some content with value to this topic =D) )
I've begun seeing a bigger picture with some of my strategy patterns, and I'm trying to decide if it's a good thing or bad thing. I'm finding that on occaision, sometimes I hit a situation where I have a
SimpleCompositor type class, lower leg of inheritance branch) that isnt exactly needed in runtime as a concrete class, but rather becomes the Strategy (Compositor type class, upper leg in inheritance branch) in a second layer of a related strategy pattern. This second layer of
ConcreteStategy classes is tied to a different Context interface (Composition type class, usually a form class in my case) than the layer above it. ( Please tell me if I make no sense here & I'll post up a diagram...)
For example 2-tier Business Objects:
First layer strategy pattern is my run of the mill business classes to interact with my forms. From my
BaseBizObj class (Strategy or "Compositor") I inherit to my
WizardBase becomes the Strategy or "Compositor" for a few
ConcreteStrategy classes, each for a specific wizard utilty. So now I have 2 layers of Strategies, with
WizardBase warping things a little bit.
I beat up on myself that something wasn't right here... didn't feel like a "classic" strategy. Then it dawned on me I've done this before without realizing it. And with a few days to stew on it, I found similar examples in Mere Mortals flavor of Codebook. In fact this layered strategy scenario is usually evident in the class diagrams where it feels like you have a cobweb when trying to lay everything out to fit on one piece of paper. Yet, when working with the concrete classes it still feels clean, like a strategy should.
So now I'm curious, what do other folks think of layered strategies like this? Too much of good thing? Am I defeating the purpose here perhaps, or is my recent conclusion that "hey this isnt just me, it is commonly found" more accurate? -- Rox
A nice article on the Strategy Pattern vs the Template:
and a Fox version one of the strategies in the article:
loApp = CREATEOBJECT("ApplicationRunner", CREATEOBJECT("ftocStrategy"))
DEFINE CLASS ApplicationRunner as Line
ItsApplication = NULL
This.ItsApplication = toApp
DO WHILE !This.ItsApplication.Done()
* needed to be sure each function is defined
DEFINE CLASS Stragety as Line
DEFINE CLASS ftocStrategy as Stragety
FileHandle = 0
IsDone = .F.
lcFile = GETFILE()
This.FileHandle = FOPEN(lcFile)
LOCAL lcFahrStr, lnFahr, lnCelcius
This.IsDone = .T.
lcFahrStr = FGETS(This.FileHandle)
lnFahr = VAL(lcFahrStr)
lnCelcius = 5.0/9.0 * (lnFahr - 32.0)
MESSAGEBOX("Fahr " + lcFahrStr + " = " + TRANSFORM(lnCelcius) + " Celcius")
See also: VFP Design Pattern Catalog
Category Design Patterns
( Topic last updated: 2002.11.01 08:33:20 PM )