Understanding the Strategy Design Pattern in C#

As I was trying to refresh my knowledge on design patterns, I spent some time revisiting the Strategy Pattern and found it to be one of the most practical and widely used patterns. I thought of sharing my thoughts here, as it can help a wider audience understand how to apply it effectively in C#.

The Strategy Pattern is a behavioural design pattern that enables selecting an algorithm dynamically at runtime. Instead of implementing multiple variations of a behaviour inside a single class (which leads to tight coupling), this pattern encapsulates different behaviours in separate classes, making the system more scalable and maintainable.

In this article, I will:

  • Explore the benefits & drawbacks of the Strategy Pattern.
  • Implement a C# example where students can choose different study strategies.
  • Enhance the implementation by dynamically selecting a strategy via user input or configuration.

Whether you're new to design patterns or looking to refine your understanding, this deep dive into the Strategy Pattern will provide valuable insights. Let’s get started! ??


Why Use the Strategy Pattern?

Software applications often need to support multiple variations of a behaviour. Without a structured approach, this can lead to:

  • Code duplication: Similar logic repeated in different places.
  • Difficult maintenance: Changes in one behaviour may impact other parts of the system.
  • Tight coupling: The main class becomes bloated with multiple conditionals (if-else or switch).

The Strategy Pattern solves this by defining a family of algorithms, encapsulating them in separate classes, and making them interchangeable.


Pros and Cons of the Strategy Pattern

? Pros

  1. Encapsulation of Behaviour Each strategy (algorithm) is isolated in its own class.
  2. Adheres to Open-Closed Principle (OCP) You can add new strategies without modifying existing code.
  3. Reduces Conditional Logic Eliminates the need for if-else statements inside the main class.
  4. Enhances Testability Strategies can be unit-tested separately.
  5. Increases Code Reusability The same strategy can be used in different contexts.

? Cons

  1. More Classes Each strategy requires a separate class, which can lead to increased complexity.
  2. Client Awareness The client must know which strategy to choose.
  3. Frequent Strategy Switching Overhead If the strategy is switched very often, object creation might introduce performance overhead.


C# Implementation: Strategy Pattern with Students and Subjects

Let’s consider a Student who can choose a Study Strategy for different subjects.


Step 1: Define the Strategy Interface

Each study method will implement this interface:

public interface IStudyStrategy
{
    void Study(string subject);
}        

Step 2: Implement Concrete Strategies

Each strategy represents a different study approach.

public class IntensiveStudyStrategy : IStudyStrategy
{
    public void Study(string subject)
    {
        Console.WriteLine($"Studying {subject} intensively for long hours with deep focus.");
    }
}

public class CasualStudyStrategy : IStudyStrategy
{
    public void Study(string subject)
    {
        Console.WriteLine($"Studying {subject} casually with short breaks and light reading.");
    }
}

public class GroupStudyStrategy : IStudyStrategy
{
    public void Study(string subject)
    {
        Console.WriteLine($"Studying {subject} in a group discussion format.");
    }
}        

Step 3: Create the Context Class (Student)

The Student class will hold a reference to a strategy and delegate the Study action to the selected strategy.

public class Student
{
    private IStudyStrategy _studyStrategy;

    public void SetStudyStrategy(IStudyStrategy studyStrategy)
    {
        _studyStrategy = studyStrategy;
    }

    public void StudySubject(string subject)
    {
        if (_studyStrategy == null)
        {
            Console.WriteLine("No study strategy selected. Defaulting to self-study.");
        }
        else
        {
            _studyStrategy.Study(subject);
        }
    }
}        

Dynamic Strategy Selection

Instead of hardcoding the strategy in Main(), let’s allow user input or configuration-based selection.

Step 4: User Input-Based Selection

using System;

class Program
{
    static void Main()
    {
        Student student = new Student();

        Console.WriteLine("Choose your study strategy:");
        Console.WriteLine("1 - Intensive Study");
        Console.WriteLine("2 - Casual Study");
        Console.WriteLine("3 - Group Study");
        Console.Write("Enter your choice: ");
        
        string choice = Console.ReadLine();
        IStudyStrategy selectedStrategy = GetStrategy(choice);

        student.SetStudyStrategy(selectedStrategy);
        student.StudySubject("Mathematics"); // Example: applying the strategy to Mathematics
    }

    static IStudyStrategy GetStrategy(string choice)
    {
        return choice switch
        {
            "1" => new IntensiveStudyStrategy(),
            "2" => new CasualStudyStrategy(),
            "3" => new GroupStudyStrategy(),
            _   => new CasualStudyStrategy() // Default strategy
        };
    }
}        

Output (User enters "1")

Studying Mathematics intensively for long hours with deep focus.        

Step 5: Configuration-Based Selection (Enterprise Applications)

In real-world applications, the strategy should be selected dynamically from a configuration file, API, or database.

Using appsettings.json (For ASP.NET Core)

{
  "StudyStrategy": "IntensiveStudy"
}        

Reading the Configuration in Code

using Microsoft.Extensions.Configuration;

public class Program
{
    public static void Main(string[] args)
    {
        var configuration = new ConfigurationBuilder()
            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
            .Build();

        string strategyName = configuration["StudyStrategy"];
        IStudyStrategy studyStrategy = GetStrategy(strategyName);

        Student student = new Student();
        student.SetStudyStrategy(studyStrategy);
        student.StudySubject("Physics");
    }

    static IStudyStrategy GetStrategy(string strategyName)
    {
        return strategyName switch
        {
            "IntensiveStudy" => new IntensiveStudyStrategy(),
            "CasualStudy" => new CasualStudyStrategy(),
            "GroupStudy" => new GroupStudyStrategy(),
            _ => new CasualStudyStrategy()
        };
    }
}        

Now, modifying the strategy is as simple as updating appsettings.json!

This makes the application more scalable and maintainable.


Final Thoughts

? Best Use Cases for the Strategy Pattern

  • When an object needs to support multiple behaviours dynamically.
  • When multiple variations of an algorithm exist.
  • When you want to reduce complex conditional logic (if-else chains).
  • When you need easier testability and maintainability.

? When NOT to Use

  • When the behaviour rarely changes.
  • When only one or two variations exist, adding extra classes could be unnecessary.


Conclusion

The Strategy Pattern is a powerful way to encapsulate behaviours into separate classes and switch them dynamically at runtime.

  • We first implemented different study strategies (IntensiveStudyStrategy, CasualStudyStrategy, etc.).
  • Then, we used dynamic selection via user input and configuration settings.

This pattern provides flexibility, modularity, and scalability while keeping the core logic clean and maintainable.

Thanks for sharing your thoughts on the Strategy Pattern in .NET! It's such a valuable design approach for creating flexible architecture. What other patterns do you find particularly useful?

回复

要查看或添加评论,请登录

Dennis John的更多文章

社区洞察

其他会员也浏览了