views:

218

answers:

3

I have a standard GOF Strategy Pattern: client code holds a reference to an AbstractStrategy, which points to any one of several ConcreteStrategies deriving from or implementing the AbstractStrategy.

One ConcreteStrategy delegates to any of several other ConcreteStrategies, depending on its passed parameters, e.g.:

public class ConcreteStrategy0 {
public void doStrategy(SomeType someData) {
   switch( somefunc(someData ) {
    case 0: ConcreteStrategy1.singleton.doStrategy(someData); break; 
    case 1: ConcreteStrategy2.singleton.doStrategy(someData); break;
    default: ConcreteStrategy3.singleton.doStrategy(someData); break;
   }
}

This isn't quite a Coplien Envelope/Letter (as the intent isn't quite the same).

But does it have a name?

A: 

It looks like a combination of Strategy and Factory method pattern. I doubt it has a name on its own. But why do you really need the switch statement? I think an Abstract Factory pattern would be nicer here.

Igor Brejc
The switch is to illustrate what the code does, not to insist upon a particular implementation. In practice however, I don't know that, within a ConcreteStrategy's implementation, I want to delegate to yet another class hierarchy of strategies.
tpdi
Oh, and it's not Factory, I don't think: Factory is an Object Creational pagttern, and nothing's being created here, much less is a subclass type being created. We're merely selectign among three alternatives.
tpdi
Well the switch statement in your example is quite prominent, so one cannot simply ignore it. And it's a part of the design. The challenge here is how to get rid of it (http://c2.com/cgi/wiki?SwitchStatementsSmell) using some other more elegant way.
Igor Brejc
BTW: even if you're returning a pre-created singleton instance, you could still use Factory patterns. The fact that the factory isn't really creating objects isn't really important for clients.
Igor Brejc
+1  A: 

It seems like an Anti-Pattern; doStrategy knows too much about concrete stategy and whenever a new concrete strategy is added, you have to update doStrategy.

Above code violates Open/Closed Principle, and I wonder if it can be named or has a name for above code.


After thoughts: When calling ConcreteStrategy0.doStrategy, you are basically calling it like

new ConcreteStrategy0().doStrategy(someData);

While it could have been better refactored as

// Inject strategy: Delegate object creation to a factory
new ConcreteStrategy0().doStrategy(StrategyFactory.Create(), someData);

// And update "doStrategy" to,
public void doStrategy(IStrategy strategy, SomeType someData)
{
    strategy.singleton.doStrategy(someData);
}

Or delegate the response to the strategy altogether,

// OR Let the strategy do stuff instead
StrategyFactory.Create().doStrategy(someData);
Sung Meister
A: 

To me it looks like a dispatcher: It takes a request, and decide base on some of the parameters to which handler to redirect it.

I agree that the implementation as is violates the open-closed principle (what happens when adding a new strategy?). A better solution will be (using spring configuration):

<bean id="client" class="...">
  <property name="strategy" ref="dispatcherStartegy"/>
</bean>
<bean id="dispatcherStartegy" class="...">
  <property name="strategies">
    <map>
      <entry key="0" ref="concreteStrategy1"/>
      <entry key="1" ref="concreteStrategy2"/>
    </map>
  </property>
  <property name="fallbackStrategy" ref="concreteStrategy3"/>
</bean>
<bean id="concreteStrategy1" ... />
<bean id="concreteStrategy2" ... />
<bean id="concreteStrategy3" ... />

And the classes will be as follows:

class Client {
  Strategy strategy;
  public void setStrategey(Strategy strategy {... }
  public void doSomething(...) {
    ...
    strategy.doStrategy(...);
    ...
  }
}

class DispatcherStrategy implements Strategy {
  Map<Integer,Strategy> strategies;
  Strategy fallbackStrategy;
  ... getters, setters ...
  public void doStrategy(...) {
    Strategy s  = strategies.get(keyFromArguments);
    if ( s==null ) {
      s = fallbackStrategy
    }
    s.doStrategy(...)
  }
}

And in this way any change in strategy will just change the configuration of the application, not the code.

David Rabinowitz