Understanding Strategy Design Pattern made Easy

In this detailed article, let's understand what is a Strategy Design Pattern and how we can implement it with an illustrating example.

What is a Strategy Pattern?

Strategy Pattern is a Behavioral Design Pattern.

It lets you define a family of algorithms in separate classes and make their objects interchangeable. It can be used when you need to create multiple variations for a solution and let the calling client decide which variation of the algorithm needs to be called over the runtime.

When do you need a Strategy pattern

Imagine you have designed an algorithm for a problem statement in your application that heavily uses a summation approach. You have developed the business logic based on this algorithm and the caller calls this code when the application runs. Everything works fine.

After a few weeks you had to design another option for the caller component which uses a subtraction approach for the same problem statement. You now have to provide the user with a new algorithm that uses a subtraction approach, apart from the existing Summation approach.

As the application grows in size, you are asked to provide multiple variants of the same base approach. To do this you may have to keep up with very long conditional switches which can become clumsy and complex as it goes.

Also you keep on modifying the existing algorithm in order to accommodate new variants, which is a violation of the Open Closed Principle.

Strategy pattern solves this by separating each variant into a separate strategy and letting the client run the required strategy by passing the respective instance during runtime.

A Context maintains an instance of a concrete strategy algorithm and communicates only via the strategy interface. Each solution has a concrete strategy implemented for each variation of the algorithm.

How to implement a Strategy pattern

For a given set of algorithms, you can design a Strategy by following the below steps –

  1. Define an abstract strategy that has a common method to contain the algorithm
  2. Each variation of the algorithm is represented by a concrete strategy which implements the abstract strategy
  3. Define a context class that maintains an instance of the strategy to be applied –
    • This class has a setter method to change the strategy on the fly.
    • It contains the business logic. It is like a gateway to the algorithm being used internally.
    • All the interactions that happen within the context class is through the abstract strategy type and not any concrete strategy type.
  4. A calling client creates an instance of this context class and passes the strategy it wants to use. The context runs the business logic and uses the strategy set by the client.

Implementing Strategy Pattern with C# Example

The below code explains how to implement a Strategy for a family of Arithmetic algorithms as explained above.

namespace DesignPatternsExamples.Patterns.Behavioral;

/// <summary>
/// An abstract Strategy that defines a template
/// </summary>
public interface IArithmeticStrategy
{
    double Calculate(double a, double b);
}

/// <summary>
/// Algorithm 1 - Summation Strategy
/// </summary>
public class SummationStrategy : IArithmeticStrategy
{
    public double Calculate(double a, double b)
    {
        return a + b;
    }
}

/// <summary>
/// Algorithm 2 - Subtraction Strategy
/// </summary>
public class SubtractionStrategy : IArithmeticStrategy
{
    public double Calculate(double a, double b)
    {
        return a - b;
    }
}

/// <summary>
/// Algorithm 3 - Multiplication Strategy
/// </summary>
public class MultiplicationStrategy : IArithmeticStrategy
{
    public double Calculate(double a, double b)
    {
        return a * b;
    }
}

/// <summary>
/// Algorithm 4 - Division Strategy
/// </summary>
public class DivisionStrategy : IArithmeticStrategy
{
    public double Calculate(double a, double b)
    {
        return a / b;
    }
}

/// <summary>
/// A Context class that runs the Strategies
/// based on the variant passed by the client
/// Doesn't know the internals about any of the strategy
/// being called over internally
/// </summary>
public class StrategyContext
{
    private IArithmeticStrategy? _aStrategy;

    public void SetStrategy(IArithmeticStrategy strategy)
    {
        _aStrategy = strategy;
    }

    public void PrintCalculation(double a, double b)
    {
        var result = _aStrategy!.Calculate(a, b);
        Console.WriteLine($"Calculated Arithmetic is {result}");
    }
}

A client finally creates an instance of the Context and runs the required strategy as below –

/// <summary>
/// A Client that runs the algorithms
/// without having to know the internals
/// </summary>
public class StrategyClient
{
    public void CallingClient()
    {
        double a = 5,
            b = 4;

        Console.WriteLine();
        Console.WriteLine();
        Console.WriteLine("***** Strategy Pattern Example *****");
        Console.WriteLine(
            "Let's define a family of algorithms and make their objects Interchangeable."
        );
        Console.WriteLine(
            "In this example, a family of Arithmetic Algorithms over two variables a,b"
        );
        Console.WriteLine("are implemented and made interchangeable based on the requirement.");
        Console.WriteLine();

        var context = new StrategyContext();

        Console.WriteLine();
        Console.WriteLine("Setting the current algorithm to Summation...");
        context.SetStrategy(new SummationStrategy());
        context.PrintCalculation(a, b);

        Console.WriteLine();
        Console.WriteLine("Setting the current algorithm to Subtraction...");
        context.SetStrategy(new SubtractionStrategy());
        context.PrintCalculation(a, b);

        Console.WriteLine();
        Console.WriteLine("Setting the current algorithm to Multiplication...");
        context.SetStrategy(new MultiplicationStrategy());
        context.PrintCalculation(a, b);

        Console.WriteLine();
        Console.WriteLine("Setting the current algorithm to Division...");
        context.SetStrategy(new DivisionStrategy());
        context.PrintCalculation(a, b);
    }
}

Output of the Implementation

When to use a Strategy Pattern

Use the Strategy pattern to isolate business logic of a Class from the implementation details of the different strategies which may not be important in the context of that class.

Some typical examples of such scenarios include designing a Navigation application where users can choose from various modes of transport, a Super app where an app must show different features based on the module selected etc.

Conclusion

Using Strategy pattern helps us in extending and providing new implementations of an existing functionality without having to modify the existing code – which is the Open/Closed SOLID Principle.

A Factory method pattern is somewhat similar to a Strategy, where a Factory method decides on which variation to choose based on user input.

Strategy is based on Object Composition, meaning the behavior changes based on the object being passed. It is different from the Template Method which does a similar thing but the types are based on Inheritance.

Fun fact – It is argued that the Provider pattern introduced in .NET Framework for extending User Identities in .NET applications is based on Strategy pattern.


Buy Me A Coffee

Found this article helpful? Please consider supporting!

Ram
Ram

I'm a full-stack developer and a software enthusiast who likes to play around with cloud and tech stack out of curiosity. You can connect with me on Medium, Twitter or LinkedIn.

Leave a Reply

Your email address will not be published. Required fields are marked *