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
}The Problem?
- 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.
- Hard to maintain: The class grows uncontrollably.
- 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);
}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.");
}
}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);
}
}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");
}
}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.Advantages of Strategy
- Eliminates conditionals: Goodbye giant
switch. The code is more readable. - Open/Closed Principle (OCP): Want to add
BicycleRoute? Create the class and you’re done. You don’t touchGPSNavigatoror the other routes. - 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.