Implementing Strategy Pattern Without Significant Branching

designdesign-patternsstrategyunit testing

The Strategy pattern works well to avoid huge if…else constructs and make it easier to add or replace functionality. However, it still leaves one flaw in my opinion. It seems like in every implementation there still needs to be a branching construct. It might be a factory or a data file. As an example take an ordering system.

Factory:

// All of these classes implement OrderStrategy
switch (orderType) {
case NEW_ORDER: return new NewOrder();
case CANCELLATION: return new Cancellation();
case RETURN: return new Return();
}

The code after this doesn't need to worry, and there is only one place to add a new order type now, but this section of code still isn't extensible. Pulling it out into a data file helps readability somewhat (debatable, I know):

<strategies>
   <order type="NEW_ORDER">com.company.NewOrder</order>
   <order type="CANCELLATION">com.company.Cancellation</order>
   <order type="RETURN">com.company.Return</order>
</strategies>

But this still adds boilerplate code to process the data file – granted, more easily unit testable and relatively stable code, but additional complexity nontheless.

Also, this sort of construct doesn't integration test well. Each individual strategy may be easier to test now, but every new strategy you add is addition complexity to test. It's less than you would have if you hadn't used the pattern, but it's still there.

Is there a way to implement the strategy pattern that mitigates this complexity? Or is this just as simple as it gets, and trying to go further would only add another layer of abstraction for little to no benefit?

Best Answer

Of course not. Even if you use an IoC container, you will have to have conditions somewhere, deciding which concrete implementation to inject. This is the nature of the Strategy pattern.

I don't really see why people think this is a problem. There are statements in some books, like Fowler's Refactoring, that if you see a switch/case or a chain of if/elses in the middle of other code, you should consider that a smell and look to move it to its own method. If the code within each case is more than a line, maybe two, then you should consider making that method a Factory Method, returning Strategies.

Some people have taken this to mean that a switch/case is bad. This is not the case. But it should reside on its own, if possible.

Related Topic