views:

398

answers:

10

When would the Strategy Pattern be used?

I see client code snippets like this:


class StrategyExample {

    public static void main(String[] args) {

        Context context;

        // Three contexts following different strategies
        context = new Context(new ConcreteStrategyAdd());
        int resultA = context.executeStrategy(3,4);

        context = new Context(new ConcreteStrategySubtract());
        int resultB = context.executeStrategy(3,4);

        context = new Context(new ConcreteStrategyMultiply());
        int resultC = context.executeStrategy(3,4);

    }

}

and it looks like you could just refactor it to this:


class StrategyExample {

    public static void main(String[] args) {
         // Three contexts following different strategies
        int resultA =new ConcreteStrategyAdd().execute(3,4);
        int resultB =new ConcreteStrategySubtract().execute(3,4);
        int resultC =new ConcreteStrategyMultiply().execute(3,4);
    }

}

The first section of code was taken directly from the wikipedia page. One big difference is the context goes away, but it wasn't doing anything in the example anyway. Maybe someone has a better example where Strategy makes sense. I usually like design patterns but this one seems to add complexity without adding usefulness.

+1  A: 

The code snippet you're quoting is a bit deceptive in that it's (slightly) out of context. What you're writing below in your example is also the strategy pattern - you've just rewritten the above example a bit more concisely.

The main point in the example is that the specifics of the mathematical operation are abstracted away from the caller. This way the caller can work with any binary operator by creating a new ConcreteStrategy, e.g.

  int mod = new ConcreteStrategy(){ 
         public int execute(int a, int b){ return a %b; }
  }.execute(3,4);
Steve B.
A: 

Areas that come to mind:

  • A resource allocator. In manual resource management, this might be minimizing the time it takes for the resource to allocate, or minimizing fragmentation. Each strategy here has an "Allocate" method that has the same interface, with the user making a decision about which strategy to use based on what they are trying to optimize.
  • A method for connecting and sending network data. Maybe in some cases you would prefer to connect and send UDP datagrams, maybe in other situatinos where performance was less of a factor you would send using TCP/IP.
  • Data formatting/serialization strategy. Allow the code to decide whether an object should be serialized with Json or with Xml. Maybe one for machines, and the other for human-readable situations. Both strategies have a "Serialize" method which takes an object. Each serializes differently.

The theme is that the decision on whether to do something one way or another way is dependent on situational factors, and you or your code would choose the correct strategy based on the situation.

Now why would this be more useful than something like:

void DoIt()
{
    if (... situation1...)
    {
       DoA()
    }
    else
    {
       DoB();
    }
}

The reason is sometimes you want to just make the decision once and forget about it. The other important theme to strategy pattern is that you decouple the decision about which strategy to use from the code that needs to execute the strategy.

DoItStrategy MakeDoItStrategy()
{
     if (... situation1...)
     {
           return new DoItStrategyA();
     }
     else
     {
           return new DoItStrategyB();
     }
}

In the last example you can just just store the strategy, passing it as just another object that implements the strategy interface. For those executing the strategy, they simply have a way to perform an action. They don't know what the inner workings are under the hood, only that the interface will be satisfied. The users of the strategy shouldn't need to know why we made a decision. They just need to do an action. We make a decision once and passed the strategy to the classes that use the strategy.

For example, consider the case where we make a program-wide decision, based on a given network configuration, to connect and send data to a remote hosts with UDP. Instead of each user of the network interface needing to know the logic to make the decision (the "DoIt" function above), we can create the UDP strategy upfront and pass it to everyone who needs to send network data. This strategy then implements a simple interface with the same end result - data gets from A to B.

Doug T.
A: 

I typically use the strategy pattern when I have a bunch of different things to do, based on the situation. In essence, it's a way of transforming a long series of if/else statements into a few lines.

One way to do this (in Java):

Map<String, Strategy> strategyMap = new HashMap<String, Strategy>();
strategyMap.put("bark", new BarkingStrategy());
strategyMap.put("meow", new MeowingStrategy());
strategyMap.put("moo", new MooingStrategy());
strategyMap.put("giraffeSound", new UnknownStrategy());

You first build up some form of repertoire of strategies.

Later...

String command = //...some form of input
strategyMap.get(command).execute();

This way, you can "generically" handle many different situations.

i.e.:

moo

would execute the MooingStrategy()

Malaxeur
A: 

The Context knows how to do some complicated thing, given some operation that you provide to it. The operations are simple things (add two numbers, multiply two numbers, etc.). A Context, in a non-trivial example, might require any number of different behaviors (not merely the one), which are best factored out as "strategies" (since trying to subclass Context would create a combinatoric explosion of subclasses).

Jonathan Feinberg
A: 

Yeah, the example is lame, but the concept of delegates with different implementation strategies is a basic, very old, design pattern that I've used in lots and lots of apps.

I think your problem here is a common one, though, almost every design pattern example I ever see is incredibly contrived and always leads me to ask questions just like yours - they always seem useless when presented this way.

M1EK
A: 

It makes more sense when the context object has more responsibilities and the strategy abstraction decouples these responsibilities from some aspect of the operation. One example, (in C#) is the IComparer interface:

interface IComparer<T>
{
    int Compare(T a, T b);
}

Which can be passed into a sorting algorithm.

eulerfx
+8  A: 

The problem with toy examples such as this is that it is often easy to miss the point. In this case the code could indeed be just implemented as you have shown. In a strategy pattern the main value is in being able to switch out different implementations for different situations.

The example you have is only illustrating the objects in the pattern and the interactions between them. Imagine instead that you had a component that renders graphs for a website depending on whether it was a real web browsers or a smart phone on the other end you would have some code that would detect the type of browser the create and set the strategy on another component that could use the strategy object in some complex code that would not need to be duplicated and would do the work in both situations leaving the details of the actual drawing of the graph to the appropriate strategy object:

interface GraphStrategy {
    Image renderGraph(Data graphData);
}

class BigGraphStratedy implements GraphStrategy {
    ...
}

class SmallGraphStrategy implements GraphStrategy {
    ...
}

Then in some other code:

GraphStrategy graphStrategy;

if (phoneBrowser == true) { 
    graphStrategy = new SmallGraphStrategy();
} else {
    graphStrategy = new BigGraphStrategy();
}

The rest of you application code can then just use graphStrategy.renderGraph() without having to know whether full or small image rendering is being performed.

Tendayi Mawushe
That's just what I was looking for. Thanks!
User1
+1  A: 

The Cocoa frameworks used on Mac and iPhone use the strategy pattern a lot, except that we call it the Delegate Pattern. Here's how we use it:

We have a concrete object, say an NSTableView. The table view needs to know how many rows it has, what goes in each row, each column etc. So instead of subclassing the tableview to provide that information, we provide a "delegate" object. That delegate object implements a certain interface ("protocol" in Objective-C). Then the tableview can simply ask its delegate object what it should do in certain situations ("how many rows do I have?" "What goes in this cell?" "Is the user allowed to select this row?"). We can swap the delegate object at runtime simply by assigning a new object that conforms to the NSTableViewDelegate protocol.

So yeah, the strategy pattern is one of my favorites and one that I use every single day.

Dave DeLong
A: 

The main difference is that in the 2nd example, the strategy is the algorithm (hence no pattern). In the 1st example, you are abstracting/isolating out a part of the algorithm.

For example, the implementation of Context.executeStrategy() could be:

public int executeStrategy(int baseValue, int exponentFactor)
{
    return (int) Math.Pow(baseValue, this.Strategy.Calculate(2, exponentFactor));
}
Joaquim Rendeiro
A: 

The strategy pattern is useful in situations where you (or users of your code) may want to change calculations in your algorithms. A simple example where I have used the strategy pattern is in modeling heuristics in an A* search. A* uses heuristics, which are simple calculations to estimate the remaining cost if a certain node (Ni) is chosen on the path to the goal node (Ng). My interface looked something like this:

class Heuristic {
public:
    virtual int estimateRemainingCost(const node &Ni, const node &Ng) const = 0;
};

And they are used like this:

...
// for each node (Ni) that is adjacent to the current node Nc
int node_priority = cost(Ni)/* the cost of choosing this node on the path */
                    + heuristic->estimateRemainingCost(Ni, Ng);
unsearched_nodes_.queue(node_priority, Ni);
...

Heuristics are strategies or calculations that can be replaced. In other words, it is a trivial exercise to change the heuristic of the search algorithm.

DerrickH