The Strategy pattern defines a family of algorithms, then makes them interchangeable by encapsulating each as an object. Consequently, the actual operation of the algorithm can vary based on other inputs, such as which client is using it.

A fire in a grill.
Fiiiiiire. (Dun dun Dun) C'mon, unh! Photo by Danny Gallegos / Unsplash

The basic idea of this pattern is that if we encapsulate behavior as objects, we can then select which object to use and, thereby, which behavior to implement based upon some external inputs or state. We further allow for many different behaviors to be implemented without creating huge if/then or switch statements.

NOTE: This post is part of a series demonstrating software design patterns using C# and .NET. The patterns are taken from the book Design Patterns by the Gang of Four. Check out the other posts in this series.

The Rundown

  • Type: Behavioral
  • Useful? 4/5 (Very)
  • Good For: Encapsulating parts of an algorithm as objects and allowing them to be invoked independently.
  • Example Code: On GitHub
exceptionnotfound/DesignPatterns
Repository for all of my Daily Design Pattern posts. - exceptionnotfound/DesignPatterns
Project for this post: Strategy

The Participants

  • The Strategy declares an interface which is implemented by all supported algorithms.
  • The ConcreteStrategy objects implement the algorithm defined by the Strategy.
  • The Context maintains a reference to a Strategy object, and uses that reference to call the algorithm defined by a particular ConcreteStrategy.

A Delicious Example

To model this pattern, let's talk about some different ways to cook food.

A grill setup, on which burgers, hot dogs, corn on the cob, kebobs, and mushrooms are cooking

When cooking various kinds of food, particularly meats, there's often more than one way to cook them to safe eating temperatures.

For example, you might grill them, bake them, deep-fry them, or broil them, depending on whether you have friends over, how much you want to show off your cooking skills, and how many burns you are willing to suffer. Each of these methods will get the item cooked, just via different processes. These processes, in object-oriented code, can each be their own class.

In our example, we will pretend that we'll ask the user what method they'd like to use to cook their food, and then implement that method using the Strategy design pattern.

First, let's write up the Strategy participant, the abstract class CookStrategy, which for our demo is an abstract class.

/// <summary>
/// The Strategy abstract class, which defines an 
/// interface common to all supported strategy algorithms.
/// </summary>
abstract class CookStrategy
{
    public abstract void Cook(string food);
}

Each strategy by which we will cook a food item must implement the method Cook(). Let's implement a few of those strategies now (these are all ConcreteStrategy participants):

/// <summary>
/// A Concrete Strategy class
/// </summary>
class Grilling : CookStrategy
{
    public override void Cook(string food)
    {
        Console.WriteLine("\nCooking " + food + " by grilling it.");
    }
}

/// <summary>
/// A Concrete Strategy class
/// </summary>
class OvenBaking : CookStrategy
{
    public override void Cook(string food)
    {
        Console.WriteLine("\nCooking " + food + " by oven baking it.");
    }
}

/// <summary>
/// A Concrete Strategy class
/// </summary>
class DeepFrying : CookStrategy
{
    public override void Cook(string food)
    {
        Console.WriteLine("\nCooking " + food + " by deep frying it");
    }
}

To complete the demo app, let's implement our Context participant, which maintains a reference to both the food we are cooking and the Strategy we are using to do so:

/// <summary>
/// The Context class, which maintains a reference to the chosen Strategy.
/// </summary>
class CookingMethod
{
    private string Food;
    private CookStrategy _cookStrategy;

    public void SetCookStrategy(CookStrategy cookStrategy)
    {
        this._cookStrategy = cookStrategy;
    }

    public void SetFood(string name)
    {
        Food = name;
    }

    public void Cook()
    {
        _cookStrategy.Cook(Food);
        Console.WriteLine();
    }
}

Finally, we can allow the user to select what food they want to cook and what Strategy they wish to use in our Main().

static void Main(string[] args)
{
    CookingMethod cookMethod = new CookingMethod();

    Console.WriteLine("What food would you like to cook?");
    var food = Console.ReadLine();
    cookMethod.SetFood(food);

    Console.WriteLine("What cooking strategy would you like to use (1-3)?");
    int input = int.Parse(Console.ReadKey().KeyChar.ToString());
            
    switch(input)
    {
        case 1:
            cookMethod.SetCookStrategy(new Grilling());
            cookMethod.Cook();
            break;

        case 2:
            cookMethod.SetCookStrategy(new OvenBaking());
            cookMethod.Cook();
            break;

        case 3:
            cookMethod.SetCookStrategy(new DeepFrying());
            cookMethod.Cook();
            break;

        default:
            Console.WriteLine("Invalid Selection!");
            break;
    }
    Console.ReadKey();
}

Let's say we want to cook some pork chops. Here's the sample program output for cooking pork chops three different ways:

Will I Ever Use This Pattern?

Probably. I find this pattern to be very useful when refactoring applications which have many different rules regarding how objects behave, particularly in our line-of-business apps which often have many different possible strategies in play. If you're only ever going to have two or three strategies for a given object, refactoring to the Strategy pattern may not be worth it, but if you could possibly have more, that's where this design pattern shines.

Strategy vs State

It is worthwhile to note that the Strategy pattern and the State pattern are similar, but are used in different ways.

  • The Strategy pattern decides on an appropriate behavior based on external (relative to the object) inputs, whereas the State pattern decides on an appropriate behavior based on the object's internal state.
  • Objects in the State pattern store a reference to the object that is in that state; no such thing occurs when using Strategy.
  • Strategies (generally) handle only a single, specific task, whereas States can be as complex as necessary to properly represent the desired state of an object.

Summary

The Strategy design pattern allows different behaviors for a given object to be used under different circumstances. This allows for many different behaviors to be implemented and tested separately, since each will be encapsulated as an object.

Happy Coding!