views:

961

answers:

8

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 calling them, and that example. What I saw was just placing two other classes into the process and cannot see where there would be a benefit. Now I'm sure I'm missing something obvious (wood for the trees) - could someone please explain it using a definitive real-world example?


What I can make from the answers so far, it seems to me to be just a more complex way of doing this:

have an abstract class: MoveAlong with a virtual method: DoIt()
have class Car inherit from MoveAlong, 
     implementing DoIt() { ..start-car-and-drive..}
have class HorseCart inherit from MoveAlong, 
     implementing DoIt() { ..hit-horse..}
have class Bicycle inherit from MoveAlong, 
     implementing DoIt() { ..pedal..}
now I can call any function taking MoveAlong as parm 
passing any of the three classes and call DoIt
Isn't this what Strategy intents? (just simpler?)

[Edit-update] The function I refer to above is replaced with another class in which MoveAlong would be attribute which is set according to need based on the algorithm implemented in this new class. (Similar to what is demonstrated in the accepted answer.)


[Edit-update] Conclusion

The Strategy Pattern has it's uses, but I am a strong believer in KISS, and would tend to more straightforward and less obfuscatory techniques. Mostly since I want to pass on easily maintainable code (and 'cos I'll most likely be the one who have to make the changes!).

+1  A: 

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 ioctls 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. :-)

Chris Jester-Young
"passed into a function that doesn't have to care which class those instances belong to" - feels like you added more trees ;-) . How's this differ from virtual functions/polymorphism/generic methods classes ?
slashmais
It _is_ virtual functions. Strategy Pattern, in my understanding, is just a very specific use of virtual functions, namely using interfaces (in the Java sense) to access functionality rather than concrete classes.
Chris Jester-Young
+6  A: 

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.

Garth Gilmour
just watched the 'Totally GridBag' bit - I can relate to it. lol
slashmais
+1  A: 

This design pattern allows to encapsulate algorithms in classes.

The class that uses the straegy, the client class, is decoupled from the algorithm implementation. You can change the algorithms implementation, or add new algorithm without having to modify the client. This can also be done dynamically: the client can choose the algorithm he will use.

For an example, imagine an application that needs to save an image to a file ; the image can be saved in defferent formats (PNG, JPG ...). The encoding algorithms will all be implemented in different classes sharing the same interface. The client class will choose one depending on the user preference.

philippe
+2  A: 

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:

  • you can change strategy at runtime - compare this to changing the class type at runtime, which is much more difficult, compiler specific and impossible for non-virtual methods
  • one main class can use more than one strategies which allows you to recombine them in multiple ways. Consider a class that walks a tree and evaluates a function based on each node and the current result. You can have a walking strategy (depth-first or breadth-first) and calculation strategy (some functor - i.e. 'count positive numbers' or 'sum'). If you do not use strategies, you will need to implement subclass for each combination of walking/calculation.
  • code is easier to maintain as modifying or understanding strategy does not require you to understand the whole main object

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:

ddimitrov
+10  A: 

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.

Gerald
So in my idea-code in the question, the function that takes MoveAlong as parm is substituted with the equivalent of your Clock-class?
slashmais
It just just struck me how similar this is to the Command Pattern where you pass in an Action on the Command's constructor.
David Robbins
passing an Action object to a Command *is* using the strategy pattern.
Mark Cidade
+3  A: 

There is a difference between strategy and decision/choice. Most of the time a we would be handling decisions/choices in our code, and realise them using if()/switch() constructs. Strategy pattern is useful when there is a need to decouple the logic/algorithm from usage.

As an example, Think of a polling mechanism, where different users would check for resources/updates. Now we may want some of the priveliged users to be notified with a quicker turnaround time or with more details. Essentailly the logic being used changes based on user roles. Strategy makes sense from a design/architecture view point, at lower levels of granularity it should always be questioned.

questzen
+1  A: 

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.

David Robbins
+1  A: 

More detail and example in both C# and JavaScript is in here: http://tech.wowkhmer.com/post/2008/11/14/Strategy-Design-Pattern.aspx

I hope this can help you to understand more.

Vorleak Chy