If you are a fan of the strategy pattern, and you find yourself adding a lot of conditional logic around each strategy then you should consider replacing all branching logic using inversion of control.
Take the following code as an example. It defines a strategy for reading different file types. For simplicity, the code writes out to the console a message, in a real-world application, the logic would be far more complex, but we are not interested in that logic, rather we are interested in how the strategy pattern works and how we can improve its usage.
Typically, there is a context class, this is the container class that decides which strategy to use. For our example, I created the following context class.
What you see above is often shown as an example of the strategy pattern, and it is often found in real-world apps. Technically, the code above is valid and can be easily maintained if you have a few strategies. Once you start to reach more than a few strategies then the code above can become a problem as every new strategy requires adding another conditional check.
An approach that I consider to be an improvement is to decide the strategy via inversion of control. Take the example above, instead of if/else or switch statements, the context class takes a collection of all available strategies, then based on the file extension the corresponding strategy is selected.
Here is how the code would look.
First, the IStrategy interface is updated by adding a new AppliesTo method. The purpose of this method is to know if the strategy can satisfy the given file type by looking at the file extension.
With the updated interface, each strategy is also updated.
And the updated context class.
The class now takes a collection of all concrete implementations of IStrategy. Look how much cleaner the context class has become, all the conditional logic is gone and the best part, adding a new strategy only involves adding the new strategy class via dependency injection. With this approach, the context class never has to change with the addition of a new strategy. Pretty neat.
By the way, the technique shown above can be applied whenever you find yourself working with interfaces that share similar functionality.