Strategy Pattern

Greetings, fellow developers! Welcome back to .Net Core Central. In today’s blog post, we delve into the fascinating world of design patterns, with a special focus on the Strategy Design Pattern – a versatile member of the Gang of Four design patterns.

Understanding the Essence of Strategy Pattern

The Strategy Design Pattern, classified under behavioral design patterns, revolves around the idea of defining a family of algorithms and encapsulating each one. The primary goal is to make these algorithms easily interchangeable, allowing variations in behavior independent of the client using them.

While the details become clearer in the implementation phase, the high-level concept involves creating a contract, implementing different algorithms on that contract, and enabling clients to seamlessly switch between implementations.

Implementing Strategy Pattern in .NET Core

Let’s take a hands-on approach to understand this pattern better. A scenario where the Strategy Design Pattern shines is when dealing with multiple algorithms, such as various encryption methods or sorting techniques. In our example, we’ll focus on sorting algorithms – Quick Sort and Bubble Sort.

Quick Sort Implementation

Firstly, we create an interface named ISortStrategy with a single method, Sort. The QuickSort class then implements this interface, showcasing a straightforward Quick Sort algorithm. This sorting technique involves dividing the array based on a midpoint, creating left and right collections, and recursively sorting them.

namespace StrategyPattern.Demo;

internal interface ISortStrategy
{
    int[] Sort(int[] inputArray);
}

internal class QuickSort : ISortStrategy
{
    public int[] Sort(int[] inputArray)
    {
        if (inputArray == null || inputArray.Length <= 1) return inputArray;

        var centerIndex = inputArray.Length / 2;
        var centerValue = inputArray[centerIndex];

        var leftPart = new List<int>();
        var rightPart = new List<int>();

        for (var i = 0; i < inputArray.Length; i++)
        {
            if (i == centerIndex) continue;
            if(inputArray[i] <= centerValue)
                leftPart.Add(inputArray[i]);
            else
                rightPart.Add(inputArray[i]);
        }

        var sorted = Sort(leftPart.ToArray()).ToList();
        sorted.Add(centerValue);
        sorted.AddRange(Sort(rightPart.ToArray()));

        return sorted.ToArray();
    }
}

Bubble Sort Implementation

Next, we implement the Bubble Sort algorithm within the BubbleSort class, adhering to the ISortStrategy interface. This simple sorting method involves comparing adjacent items and swapping them based on their order.

namespace StrategyPattern.Demo;

internal class BubbleSort : ISortStrategy
{
    public int[] Sort(int[] inputArray)
    {
        var returnArray = inputArray;
        var length = returnArray.Length;
        for (int i = 0; i < length - 1; i++)
            for(int j = 0; j < length - i -1; j++)
            {
                if(returnArray[j] > returnArray[j + 1])
                {
                    var temp = returnArray[j];
                    returnArray[j] = returnArray[j + 1];
                    returnArray[j + 1] = temp;
                }
            }

        return returnArray;
    }
}

The Client: Sorted Numbers

To tie it all together, we introduce a client class named SortedNumbers. This class utilizes the Strategy Pattern by accepting a ISortStrategy in its constructor. The Sort method of this class then delegates the sorting operation to the injected strategy, providing flexibility in choosing the sorting algorithm.

namespace StrategyPattern.Demo;

internal interface ISortedNumbers
{
    void Sort(int[] items);
}

internal class SortedNumbers : ISortedNumbers
{
    private readonly ISortStrategy sortStrategy;

    public SortedNumbers(ISortStrategy sortStrategy)
    {
        this.sortStrategy = sortStrategy;
    }

    public void Sort(int[] items)
    {
        var sorted = sortStrategy.Sort(items);
        foreach (var item in sorted)
            Console.WriteLine(item);
    }
}

Running the Strategy Pattern

In the Program class, we create an array of random numbers and instantiate the SortedNumbers class twice. Once with Bubble Sort as the strategy and once with Quick Sort. By calling the Sort method, we observe the sorted output based on the chosen strategy.

using StrategyPattern.Demo;

var items = new[] { 88, 54, 34, 87, 15, 8, 105 };

var sortedNumbers = new SortedNumbers(new BubbleSort());
sortedNumbers.Sort(items);

Console.WriteLine("======================");

sortedNumbers = new SortedNumbers(new QuickSort());
sortedNumbers.Sort(items);

Conclusion: Where Strategy Shines

The Strategy Design Pattern excels in scenarios where multiple algorithms are needed. In our example, we demonstrated how it effortlessly handles sorting with different strategies. By following SOLID principles, especially the Single Responsibility Principle, the implementation is clean, maintainable, and extensible.

And there you have it – a deeper insight into the Strategy Design Pattern in .NET Core. If you found this exploration enlightening, don’t forget to go through the rest of my blog posts.

For more enlightening content, consider going to my YouTube channel and subscribing to .Net Core Central. Thank you for joining me on this journey through design patterns!