An Absolute Beginner's Tutorial for Understanding and Implementing the Strategy Pattern in C#

An Absolute Beginner's Tutorial for Understanding and Implementing the Strategy Pattern in C#

The aim of this article is to understand the basics of the Strategy pattern, explore scenarios where it can be useful, and create a rudimentary implementation for better understanding. This is not intended to demonstrate how to implement the Strategy pattern in real-world applications, and the example used is deliberately simple and far from real-world scenarios. The purpose of this article is purely to illustrate the concept of the Strategy pattern.

Background

There are many scenarios in application development where there are multiple ways to perform the same operation. We want our application to support all these options seamlessly.

For example, consider payment options on e-commerce portals. A user can pay using net banking, a credit card, or PayPal. Each option represents a different way of performing the same operation, with distinct application logic.

Another example is sorting. A sequence can be sorted using various sorting algorithms.

In situations where users need the flexibility to choose among multiple approaches for an operation, the Strategy pattern is a suitable design choice.

The Strategy pattern's philosophy is to provide multiple strategies for executing an operation and allow the user (or an algorithm) to select the appropriate one. The Gang of Four (GoF) defines the Strategy pattern as:

"Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it."


Article content

Class Diagram Components

  1. Strategy: The interface common to all algorithms. The Context uses this interface to perform operations.
  2. ConcreteStrategy: The class that implements the actual algorithm.
  3. Context: The client application that decides which strategy to use and performs the operation via the Strategy interface.

Using the Code

Let’s create a toy application for converting audio files from WAV to MP3 format. The user will select a source .wav file and choose the quality of the target MP3 file (low, average, or high).

In our code:

  • Each quality option will be implemented as a separate strategy.
  • The core conversion logic will remain unaffected by the details of these strategies, ensuring flexibility and maintainability.

Note: This is a dummy application for demonstration purposes. The design choices are solely to illustrate the Strategy pattern and may not be ideal for real-world applications.

Step 1: Define the IWavToMp3ConvertionStrategy Interface

This interface will be implemented by all concrete strategies.

interface IWavToMp3ConvertionStrategy
{
    void Convert();
}        

Step 2: Implement Concrete Strategies

Here, each strategy defines the logic for a specific quality of conversion.

// Strategy class for low-quality conversion
class LowQualityConversionStrategy : IWavToMp3ConvertionStrategy
{
    public void Convert()
    {
        Console.WriteLine("Low-quality conversion performed");
    }
}

// Strategy class for average-quality conversion
class AverageQualityConversionStrategy : IWavToMp3ConvertionStrategy
{
    public void Convert()
    {
        Console.WriteLine("Average-quality conversion performed");
    }
}

// Strategy class for high-quality conversion
class HighQualityConversionStrategy : IWavToMp3ConvertionStrategy
{
    public void Convert()
    {
        Console.WriteLine("High-quality conversion performed");
    }
}        

Step 3: Create the Context Class

The WavToMP3Convertor class represents the Context in the Strategy pattern.

public class WavToMP3Convertor
{
    AudioFile m_fileData = null;
    IWavToMp3ConvertionStrategy m_Convertor = null;

    public WavToMP3Convertor(AudioFile fileData)
    {
        m_fileData = fileData;            
    }

    public void Convert(IWavToMp3ConvertionStrategy convertor)
    {
        m_Convertor = convertor;
        m_Convertor.Convert();            
    }
}        

In this class:

  • The presentation layer passes the file data to the WavToMP3Convertor.
  • A concrete strategy instance is also passed during conversion.
  • The Context remains unaffected by the addition or removal of strategies.

Step 4: Implement the Presentation Layer

The presentation layer allows the user to select the conversion strategy.

static void Main(string[] args)
{
    IWavToMp3ConvertionStrategy selectedStrategy = null;

    Console.WriteLine("Assuming the file for conversion has been selected already");
    AudioFile file = new AudioFile { Title = "Sample File" };

    // Emulate the selection of quality
    Console.WriteLine("Enter the type of output: \n1. Low Quality\n2. Average Quality\n3. High Quality");
    int choice = Console.Read();

    // Select the strategy based on the user's choice
    if (choice == '1')
    {
        selectedStrategy = new LowQualityConversionStrategy();                
    }
    else if (choice == '2')
    {
        selectedStrategy = new AverageQualityConversionStrategy();                
    }
    else if (choice == '3')
    {
        selectedStrategy = new HighQualityConversionStrategy();                
    }

    // Perform the conversion
    if (selectedStrategy != null)
    {
        WavToMP3Convertor convertor = new WavToMP3Convertor(file);
        convertor.Convert(selectedStrategy);
    }
}        

Note: In real-world applications, concrete strategies are often created using a factory or service locator pattern, not via switch statements.

Comparing with the GoF Class Diagram

In our example:

Article content

  • IWavToMp3ConvertionStrategy is the Strategy interface.
  • WavToMP3Convertor is the Context class.
  • LowQualityConversionStrategy, AverageQualityConversionStrategy, and HighQualityConversionStrategy are Concrete Strategies.

The Context class (WavToMP3Convertor) remains unaffected by changes to strategies, illustrating the core benefit of the Strategy pattern.

Points of Interest

In this article, we explored the basics of the Strategy pattern and its ability to decouple client code from specific implementations.

Key takeaways:

  • The Strategy pattern enhances flexibility by allowing interchangeable algorithms.
  • The Context class remains unaffected by the addition or removal of strategies.

This article is written from an absolute beginner's perspective to demonstrate the pattern in action. I hope you find it informative!

#CSharpProgramming#DesignPatterns#StrategyPattern#SoftwareDevelopment

To view or add a comment, sign in

More articles by Rahul Singh

Insights from the community

Others also viewed

Explore topics