views:

248

answers:

3

The following code sample is an implementation of the Strategy pattern copied from Wikipedia. My full question follows it...

The Wiki's main method:

//StrategyExample test application

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);

    }

}

The pattern pieces:

// The classes that implement a concrete strategy should implement this

// The context class uses this to call the concrete strategy
interface Strategy {

    int execute(int a, int b);

}

// Implements the algorithm using the strategy interface
class ConcreteStrategyAdd implements Strategy {

    public int execute(int a, int b) {
        System.out.println("Called ConcreteStrategyA's execute()");
        return a + b;  // Do an addition with a and b
    }

}

class ConcreteStrategySubtract implements Strategy {

    public int execute(int a, int b) {
        System.out.println("Called ConcreteStrategyB's execute()");
        return a - b;  // Do a subtraction with a and b
    }

}

class ConcreteStrategyMultiply implements Strategy {

    public int execute(int a, int b) {
        System.out.println("Called ConcreteStrategyC's execute()");
        return a  * b;   // Do a multiplication with a and b
    }

}

// Configured with a ConcreteStrategy object and maintains a reference to a Strategy object
class Context {

    private Strategy strategy;

    // Constructor
    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    public int executeStrategy(int a, int b) {
        return strategy.execute(a, b);
    }

}

Considering specifically the above example, is the Context class redundant?

For example, I can come up with the following alternate main implementation by using the existing classes and interface except Context and it will work exactly the same. It is still loosely coupled.

(( Edit: In this simple scenario, when I leave out the Context class, will I be making a future mistake? ))

public static void main(String[] args) {

    IStrategy strategy;

    // Three strategies
    strategy = new ConcreteStrategyAdd();
    int resultA = strategy.executeStrategy(3,4);

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

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

}

Summary Update

Listing in point form what was discovered through answers and comments:

  • The Context allows for variation in how the composited Strategy is used (e.g. timing of it's call). Different Contexts might do different internal work before and after calling the given Strategy.
  • The context is a high level "black box". The Context logic can change, also the composited Strategy can change (or a different one used) without breaking the client because the client understands only how to call the context.
  • Even though I created an alternate implementation of the Wikipedia sample code by leaving out the Context, and although it worked the same as the original, the entire situation was simplified (in both cases) and my changes actually meant: 1. it's not a Strategy pattern anymore, 2. I miss the benefits of the spirit of the Strategy pattern that are mentioned here.
  • My alternate implementation used the main method like a Context, so I might as well keep the Context if effectively simulating it. By creating an impure Strategy pattern, confusion was created. I didn't need to reinvent the wheel or try to be smarter (in this case).

If any other points would be useful or if this needs correction leave a comment and I'll modify the list accordingly.

+1  A: 

It might be for this made-up example, but then I wouldn't call this the ne plus ultra of Strategy.

The Context class is demonstrating how you can give a class different behavior simply by passing in a new concrete implementation of the interface. Since the class only knows the interface, nothing has to change. That's the point. Don't take the rest of the example too literally.

The way you coded it will work, but the point is that you've dumped this into a main method. That won't be the way you'll typically use Strategy. You'll be doing it within a class, and Context is a simple example of that.

duffymo
I see what you mean. I was effectively substituting a main method for the Context class, which begs the question of why not just use a Context class like the Strategy pattern defines, in order to abstract away strategy usage as you described. This is helpful to me because I can think of it from the perspective of what I wanted to put in the main method to "simplify" things (e.g. do them slightly wrong) and realize that's the stuff I can put in a context. Thanks.
John K
`I added a Summary Update section beneath the original question to consolidate findings.`
John K
A: 

This is the better example of how the real "Context " class can look in this scenario:

class Accumulator {
    private Strategy strategy; 

    public Accumulator(Strategy strategy) { 
        this.strategy = strategy; 
    } 

    public int accumulate(List<Integer> values) { 
        int result = values.get(0);
        for (int i = 1; i < values.size(); i++) {
           result = strategy.execute(result, values.get(i));
        }
        return result;
    } 
}

EDIT: Typo in constructor fixed

axtavt
Am I seeing right, the class name is "Accumulator" and the constructor name is "Context"? I come from the C# world so maybe I'm mistaking something here and missing your point.
John K
Looks like the constructor has a typo.
Paul Morie
+4  A: 

As the name suggests, the Context is what encapsulates the point at which the strategy is performed. Without that, you just have a naked Strategy, and the calling class now picks up an extra responsibility: knowing when to call the Strategy itself. Your example is perhaps a bit too simple, and in this particular case, I would say that the Context isn't getting you too much.

An example that perhaps better illustrates the usefulness of a Context is more like the following:

public class LoadingDock {   // Context.
  private LoadStrategy ls;   // Strategy.

  public void setLoadStrategy(LoadStrategy ls) { ... }

  // Clients of LoadingDock use this method to do the relevant work, rather
  // than taking the responsibility of invoking the Strategy themselves.
  public void shipItems(List<ShippingItem> l) {
    // verify each item is properly packaged     \
    // ...                                        |  This code is complex and shouldn't be
    // verify all addresses are correct           |  subsumed into consumers of LoadingDock.
    // ...                                        |  Using a Context here is a win because
    // load containers onto available vehicle     |  now clients don't need to know how a
    Vehicle v = VehiclePool.fetch();        //    |  LoadingDock works or when to use a
    ls.load(v, l);                          //   /   LoadStrategy.
  }
}

Notice how the Strategy will never be called directly from an external client. Only shipItems uses the strategy, and the details of the steps it follows are a black box. This allows the Context to adjust how it uses the strategy without affecting clients. For instance, the steps could be completely reordered or adjusted (or removed entirely) to meet performance objectives or other goals -- but for the client, the external interface of shipItems() looks exactly the same.

Notice, also, that our example Context, the LoadingDock, could change its LoadStrategy at any time based on its internal state. For example, if the dock is getting too full perhaps it will switch to a more aggressive scheduling mechanism that gets crates off the dock and into trucks faster, sacrificing some efficiency in doing so (maybe the trucks don't get loaded up as efficiently as they could have been).

John Feminella
This is a thoroughly detailed explanation that clearly outlines importance of the context, including supporting code sample. Great.
John K
I suppose furthermore a Context class could be subclassed to append behaviour to the base context work (i.e. call back to the base context processing first before doing its additional work). The same could be done with any concrete strategy. This would allow the parts to vary behaviour independently of one another but still be compatible by base Types. So I'm starting to realize there's a lot of flexibility both horizontally through alternate implementations and vertically through inheritance, also combinations of those. Interesting. Things could also get complicated fast if not thought out.
John K
Hmm. I'm not sure I like the idea of a polymorphic `Context`. IMO, it'd be better to compose the different pieces into a single coherent `Context`, and then have appropriate strategies for each as applicable.
John Feminella
@John Feminella: That's fair and promotes less confusion than using polymorphic behaviour.`Also I added a Summary Update section beneath the original question to consolidate findings.`
John K