patron-strategy

Strategy Pattern: Swap Algorithms

  • 5 min

The Strategy is a pattern that encapsulates compatible algorithms behind a common interface. The context can receive one or another and change its behavior without knowing its details.

We’ve all written a monstrous method full of conditionals at some point. Imagine a discount system in a store:

// ❌ The hell of conditionals
public double CalculateDiscount(string customerType, double total)
{
    if (customerType == "Normal") {
        return total;
    } 
    else if (customerType == "VIP") {
        return total * 0.90;
    }
    else if (customerType == "Employee") {
        return total * 0.50; // Big discount!
    }
    else if (customerType == "BlackFriday") {
        // ... complex logic ...
    }
    // ... and so on into infinity
}
Copied!

The Problem?

  1. Violates the Open/Closed Principle: Every time you want to add a new discount type, you have to open this class and modify it, risking breaking what already worked.
  2. Hard to maintain: The class grows uncontrollably.
  3. Rigid: The algorithm is “tattooed” into the class.

The Strategy pattern tells us: “Define a family of algorithms, encapsulate each one, and make them interchangeable”.

Instead of having a single method that does everything, we have several small classes (Strategies) that do one thing, and a main object (Context) that uses one of them.

The Example: A GPS Navigator

Imagine we are programming Google Maps’ GPS. The user selects an origin and a destination, and we have to calculate the route.

But the route is not calculated the same way if you go by Car, Walking, or Bicycle.

  • The car looks for fast roads.
  • The pedestrian can use parks and sidewalks.
  • The bicycle looks for bike lanes and avoids highways.

The Solution with Strategy

Instead of a switch(transportType), we create a common interface IRouteStrategy. Then, we create a class for each route algorithm.

  • Context (GPSNavigator): The class that uses the strategy. It holds a reference to the interface.
  • Strategy (IRouteStrategy): The common interface.
  • Concrete Strategies (CarRoute, WalkingRoute…): The specific implementations.

Implementation in C#

Let’s see how this cleans up our code in a spectacular way.

We define the contract that all algorithms must fulfill.

public interface IRouteStrategy
{
    void BuildRoute(string origin, string destination);
}
Copied!

Each class implements its own logic. They are small, clean, and easy-to-test classes.

public class CarRoute : IRouteStrategy
{
    public void BuildRoute(string origin, string destination)
    {
        Console.WriteLine($"🚗 Calculating CAR route from {origin} to {destination} via highway.");
    }
}

public class WalkingRoute : IRouteStrategy
{
    public void BuildRoute(string origin, string destination)
    {
        Console.WriteLine($"🚶 Calculating WALKING route from {origin} to {destination} through pedestrian zones.");
    }
}

public class PublicTransportRoute : IRouteStrategy
{
    public void BuildRoute(string origin, string destination)
    {
        Console.WriteLine($"🚌 Calculating BUS route from {origin} to {destination} checking schedules.");
    }
}
Copied!

The navigator doesn’t know how the route is calculated. It just knows it has a strategy and executes it. The key here is the SetStrategy method, which allows changing the behavior at runtime.

public class GPSNavigator
{
    private IRouteStrategy _strategy;

    // We can assign an initial strategy
    public GPSNavigator(IRouteStrategy initialStrategy)
    {
        _strategy = initialStrategy;
    }

    // THE KEY POINT: We allow changing the strategy "on the fly"
    public void SetStrategy(IRouteStrategy newStrategy)
    {
        Console.WriteLine("--- Changing transport mode ---");
        _strategy = newStrategy;
    }

    public void ExecuteRoute(string A, string B)
    {
        if (_strategy == null)
        {
            Console.WriteLine("⚠️ No transport mode selected.");
            return;
        }
        
        // Delegate the work to the current strategy
        _strategy.BuildRoute(A, B);
    }
}
Copied!
class Program
{
    static void Main(string[] args)
    {
        // 1. Start the GPS in Car mode
        var gps = new GPSNavigator(new CarRoute());
        gps.ExecuteRoute("Madrid", "Barcelona");

        // 2. The user parks and wants to walk to the hotel.
        // We CHANGE the algorithm without creating a new GPS object.
        gps.SetStrategy(new WalkingRoute());
        gps.ExecuteRoute("Parking", "Hotel");

        // 3. The next day they go by bus
        gps.SetStrategy(new PublicTransportRoute());
        gps.ExecuteRoute("Hotel", "Museum");
    }
}
Copied!

Output:

🚗 Calculating CAR route from Madrid to Barcelona via highway.
--- Changing transport mode ---
🚶 Calculating WALKING route from Parking to Hotel through pedestrian zones.
--- Changing transport mode ---
🚌 Calculating BUS route from Hotel to Museum checking schedules.
Copied!

Advantages of Strategy

  1. Eliminates conditionals: Goodbye giant switch. The code is more readable.
  2. Open/Closed Principle (OCP): Want to add BicycleRoute? Create the class and you’re done. You don’t touch GPSNavigator or the other routes.
  3. Hot swapping: You can change the object’s behavior while the application is running (like when a game changes the enemy’s AI from “Passive” to “Aggressive” if you shoot it).

When to use it?

  • When you have many classes that only differ in how they execute a certain behavior.
  • When you need different variants of an algorithm.
  • When a class has a huge conditional that chooses between different execution branches.