Question

What is the advantage of passing in a strategy to execute as a method argument as opposed to having the implementation in an explicit method? For example, consider this calculator class:

Edited to include IOperation interface

public class Calculator
{
    public double DoOperation(IOperation operation, double num1, double num2)
    {
        return operation.Execute(num1, num2);
    }
}

public interface IOperation
{
    double Execute(double num1, double num2);
}

public class AddOperation : IOperation
{
    public override double Execute(double num1, double num2)
    {
        return num1 + num2;
    }
}

// Leaving out implementations for SubtractOperation, DivideOperation, and MultiplyOperation

As opposed to:

public class Calculator
{
    public double Add(double num1, double num2)
    {
        return num1 + num2;
    }

    public double Subtract(double num1, double num2)
    {
        return num1 - num2;
    }

    public double Multiply(double num1, double num2)
    {
        return num1 * num2;
    }

    public double Divide(double num1, double num2)
    {
        return num1 / num2;
    }   
}

With the strategy pattern, if you need to add an operation, you must create and test a new class. In the second example, you must create and test a new method. Seems like the same difference to me. In fact, I prefer the explicit method name version better because I think it's easier to understand.

There is only one advantage I can think of for the strategy pattern. That is, if you are developing a framework for other people to use, and you really can't determine what strategy other people might implement.

In all other cases, there are always a finite number of strategies that can be implemented. So, what advantage does the strategy pattern have over the explicit method name 'pattern'?

Edit

Thanks to Markus, there is another advantage that I can think of to the above pattern. In the first example, if the DoOperation() method performed a more complex algorithm, and only used the strategy to factor out the differing code, then this would be an example of DRY. And if the differing code is factored out into strategies, then there is a clear benefit to being able to test the strategies and the common part in isolation.

Was it helpful?

Solution

Your sample is a pretty short one that supports the perceiption that using a Strategy is not too much value over the separate-method-approach. However, in a more complex situation the DoOperation method will be much more complex und call the Strategy only at specific points in its algorithm.

In this case you might want to test the Context algorithm (in your case the Calculator class) separately without mixing the tests with specific implementations in the methods. The pattern enables you to separate these algorithms and test them separately. If a test fails, you can easily identify the block of code that led to the failure. If you follow the separate-method-approach, the probability is high that the methods share some common (or worse: duplicate) code that is located in the Context in the pattern. In this case, if you receive a test failure, it is harder to identify the part of code that leads to the failure.

In addition, implementing a Strategy supports the Open-Closed-principle as the Context class does not need to be changed if you add another Strategy. Also the existing Strategies remain unchanged.

Another advantage of the Strategy pattern is that you can change the Strategy at run time. Typically, the current Strategy is published as a property of the Context. If for some reason you decide that you need to change the behavior of the Context, you simply create the new Strategy and assign it to the property of the Context. If you follow the separate-method-approach, you decide at compile time which method you want to call. Of course you can add some if statements to react on some conditions. If using a Strategy, you can assign another Strategy to the Context and do not need to use if statements. This reduces the number of cases you need to test.

Which approach suits a specific situation better depends on the situation, but there are definitely strong reasons to implement a Strategy.

OTHER TIPS

If you are only ever going to add/subtract, multiply and divide then you can probably just have those 4 methods.

but what if you ever want to add new operations in the future (like advanced calculus operations)? Instead of adding even more methods which would break all preexisting users of the Calculator class, you or someone else can just create a new implementation of the Operation Strategy to perform that operation and pass it to your calculator and it will just work.

This way, the calculator class and any other classes that use the Calculator class's API won't break and you get to add new operations in the future, or even on the fly.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top