I\'ve looked at this explanation on Wikipedia, specifically the C++ sample, and fail to recognize the difference between just defining 3 classes, creating instances and call
The point is to separate algorithms into classes that can be plugged in at runtime. For instance, let's say you have an application that includes a clock. There are many different ways that you can draw a clock, but for the most part the underlying functionality is the same. So you can create a clock display interface:
class IClockDisplay
{
public:
virtual void Display( int hour, int minute, int second ) = 0;
};
Then you have your Clock class that is hooked up to a timer and updates the clock display once per second. So you would have something like:
class Clock
{
protected:
IClockDisplay* mDisplay;
int mHour;
int mMinute;
int mSecond;
public:
Clock( IClockDisplay* display )
{
mDisplay = display;
}
void Start(); // initiate the timer
void OnTimer()
{
mDisplay->Display( mHour, mMinute, mSecond );
}
void ChangeDisplay( IClockDisplay* display )
{
mDisplay = display;
}
};
Then at runtime you instantiate your clock with the proper display class. i.e. you could have ClockDisplayDigital, ClockDisplayAnalog, ClockDisplayMartian all implementing the IClockDisplay interface.
So you can later add any type of new clock display by creating a new class without having to mess with your Clock class, and without having to override methods which can be messy to maintain and debug.
The strategy pattern allows you to exploit polimorphism without extending your main class. In essence, you are putting all variable parts in the strategy interface and implementations and the main class delegates to them. If your main object uses only one strategy, it's almost the same as having an abstract (pure virtual) method and different implementations in each subclass.
The strategy approach offers some benefits:
The drawback is that in many cases, the strategy pattern is an overkill - the switch/case operator is there for a reason. Consider starting with simple control flow statements (switch/case or if) then only if necessary move to class hierarchy and if you have more than one dimensions of variability, extract strategies out of it. Function pointers fall somewhere in the middle of this continuum.
Recommended reading:
One way to look at this is when you have a variety of actions you want to execute and those actions are determined at runtime. If you create a hashtable or dictionary of strategies, you could retrieve those strategies that correspond to command values or parameters. Once your subset is selected, you can simply iterate the list of strategies and execute in succession.
One concrete example would be calculation the total of an order. Your parameters or commands would be base price, local tax, city tax, state tax, ground shipping and coupon discount. The flexibility come into play when you handle the variation of orders - some states will not have sales tax, while other orders will need to apply a coupon. You can dynamically assign the order of calculations. As long as you have accounted for all your calculations, you can accommodate all combinations without re-compiling.
In the Wikipedia example, those instances can be passed into a function that doesn't have to care which class those instances belong to. The function just calls execute
on the object passed, and know that the Right Thing will happen.
A typical example of the Strategy Pattern is how files work in Unix. Given a file descriptor, you can read from it, write to it, poll it, seek on it, send ioctl
s to it, etc., without having to know whether you're dealing with a file, directory, pipe, socket, device, etc. (Of course some operations, like seek, don't work on pipes and sockets. But reads and writes will work just fine in these cases.)
That means you can write generic code to handle all these different types of "files", without having to write separate code to deal with files versus directories, etc. The Unix kernel takes care of delegating the calls to the right code.
Now, this is Strategy Pattern as used in kernel code, but you didn't specify that it had to be user code, just a real world example. :-)
Strategy pattern works on simple idea i.e. "Favor Composition over Inheritance" so that strategy/algorithm can be changed at run time. To illustrate let's take an example where in we need to encrypt different messages based on its type e.g. MailMessage, ChatMessage etc.
class CEncryptor
{
virtual void encrypt () = 0;
virtual void decrypt () = 0;
};
class CMessage
{
private:
shared_ptr<CEncryptor> m_pcEncryptor;
public:
virtual void send() = 0;
virtual void receive() = 0;
void setEncryptor(cost shared_ptr<Encryptor>& arg_pcEncryptor)
{
m_pcEncryptor = arg_pcEncryptor;
}
void performEncryption()
{
m_pcEncryptor->encrypt();
}
};
Now at runtime you can instantiate different Messages inherited from CMessage (like CMailMessage:public CMessage) with different encryptors (like CDESEncryptor:public CEncryptor)
CMessage *ptr = new CMailMessage();
ptr->setEncryptor(new CDESEncrypto());
ptr->performEncryption();
In Java you use a cipher input stream to decrypt like so:
String path = ... ;
InputStream = new CipherInputStream(new FileInputStream(path), ???);
But the cipher stream has no knowledge of what encryption algorithm you intend to use or the block size, padding strategy etc... New algorithms will be added all the time so hardcoding them is not practical. Instead we pass in a Cipher strategy object to tell it how to perform the decryption...
String path = ... ;
Cipher strategy = ... ;
InputStream = new CipherInputStream(new FileInputStream(path), strategy);
In general you use the strategy pattern any time you have any object that knows what it needs to do but not how to do it. Another good example is layout managers in Swing, although in that case it didnt work out quite as well, see Totally GridBag for an amusing illustration.
NB: There are two patterns at work here, as the wrapping of streams in streams is an example of Decorator.