Flexible Behaviors with the Strategy Pattern

Choose your logic at runtime without cluttering your code

Posted by Hüseyin Sekmenoğlu on April 19, 2021 Design Patterns

🤔 What Is the Strategy Pattern?

The Strategy Pattern is a behavioural design pattern that allows you to define a family of algorithms, encapsulate each one and make them interchangeable at runtime. Instead of hardcoding behaviour into a class, the pattern enables selecting behaviour dynamically.

Think of it like changing the route calculation method in a navigation app from “shortest” to “fastest” or “no tolls”.  All without changing the app itself.


🧭 When to Use It

Use the Strategy Pattern when:

  • You have multiple ways to perform an action and the best one should be chosen at runtime

  • You want to eliminate conditional logic that selects different algorithms

  • You need to allow users or systems to customize how a task is performed

Common examples:

  • Payment processing (credit card, PayPal, crypto)

  • Sorting data using different algorithms

  • Logging strategies for file, console or cloud


🛠️ How It Works

The pattern includes:

  1. A Strategy Interface that defines a behaviour

  2. One or more Concrete Strategies implementing the interface

  3. A Context that uses a strategy and delegates the work to it

C# Example: Sorting Strategies

// Strategy Interface
public interface ISortStrategy
{
    void Sort(List<int> list);
}

// Concrete Strategy 1
public class BubbleSort : ISortStrategy
{
    public void Sort(List<int> list)
    {
        Console.WriteLine("Using Bubble Sort");
        // Simulate sorting
    }
}

// Concrete Strategy 2
public class QuickSort : ISortStrategy
{
    public void Sort(List<int> list)
    {
        Console.WriteLine("Using Quick Sort");
        // Simulate sorting
    }
}

// Context
public class SortContext
{
    private ISortStrategy _strategy;

    public SortContext(ISortStrategy strategy)
    {
        _strategy = strategy;
    }

    public void SetStrategy(ISortStrategy strategy)
    {
        _strategy = strategy;
    }

    public void ExecuteSort(List<int> list)
    {
        _strategy.Sort(list);
    }
}

Usage

var list = new List<int> { 3, 1, 4, 1, 5 };
var context = new SortContext(new BubbleSort());
context.ExecuteSort(list);

context.SetStrategy(new QuickSort());
context.ExecuteSort(list);

✅ Advantages

  • Promotes open/closed principle: add new strategies without modifying existing code

  • Eliminates messy if-else or switch statements

  • Makes unit testing individual algorithms easier

  • Encourages composition over inheritance


❌ Disadvantages

  • Can introduce extra complexity if overused

  • Client must be aware of different strategies and choose appropriately

  • May lead to too many small classes


🧪 Testing Benefits

  • You can test each strategy in isolation

  • Context logic becomes simpler and easier to mock

  • Easier to inject strategies using dependency injection


🌍 Real-World Use Cases

  • Authentication: Choose between OAuth, JWT or SAML providers

  • Compression tools: Select between ZIP, GZip or custom compression

  • Pricing engines: Apply seasonal, promotional or loyalty-based discounts

  • Game development: Different AI behaviors for enemy movement


🔗 Related Patterns

  • State: Similar structure but focuses on object state transitions

  • Command: Encapsulates a request as an object, while Strategy encapsulates an algorithm

  • Decorator: Adds behavior to objects without modifying their class


🎯 Final Thoughts

The Strategy Pattern gives you total control over behavior while keeping your codebase clean and extensible. It is one of the most practical patterns you can apply, especially when business logic is expected to vary or grow over time.