Press "Enter" to skip to content

Strategy Design Pattern in C#

Last updated on September 15, 2023

What it is

The Strategy Design Pattern can be used when you want to perform a function, but you might use different techniques.

For a non-computer example, let’s say I want some food to eat. There are several ways I can accomplish that: cook a meal in my kitchen, go to a restaurant, order food to be delivered to me, etc.

All those strategies perform the “function” I want – “get food”. But they do them differently.

 

Video version of demonstration

 

Examples

For this example, I’ll show a calculator class that needs to calculate the average of a list of numbers, using either the “mean” or “median” technique of averaging. The “strategy” is the different way to compute the average.

 

Non-Pattern Versions

 

Non-pattern version (multiple methods)

This version shows how the calculator class could be written using different functions – one for each averaging technique.

using System.Collections.Generic;
using System.Linq;
namespace Engine.StrategyPattern.NonPatternVersion_MultipleMethods
{
    public class Calculator
    {
        public double CalculateAverageByMean(List<double> values)
        {
            return values.Sum() / values.Count;
        }
        public double CalculateAverageByMedian(List<double> values)
        {
            var sortedValues = values.OrderBy(x => x).ToList();
            if(sortedValues.Count % 2 == 1)
            {
                return sortedValues[(sortedValues.Count - 1) / 2];
            }
            return (sortedValues[(sortedValues.Count / 2) - 1] + 
                sortedValues[sortedValues.Count / 2]) / 2;
        }
    }
}

 

Non-pattern version (single methods)

This is how the class could be written, using a single function with a “switch” statement.

using System;
using System.Collections.Generic;
using System.Linq;
namespace Engine.StrategyPattern.NonPatternVersion_SingleMethod
{
    public class Calculator
    {
        public enum AveragingMethod
        {
            Mean,
            Median
        }
        public double CalculateAverage(List<double> values, AveragingMethod averagingMethod)
        {
            switch(averagingMethod)
            {
                case AveragingMethod.Mean:
                    return values.Sum() / values.Count;
                case AveragingMethod.Median:
                    var sortedValues = values.OrderBy(x => x).ToList();
                    if(sortedValues.Count % 2 == 1)
                    {
                        return sortedValues[(sortedValues.Count - 1) / 2];
                    }
                    return (sortedValues[(sortedValues.Count / 2) - 1] + 
                        sortedValues[sortedValues.Count / 2]) / 2;
                default:
                    throw new ArgumentException("Invalid averagingMethod value");
            }
        }
    }
}

 

Design Pattern Version

 

Strategy Design Pattern version

The version using the design pattern is a little more complex. It uses four classes/interfaces, while the non-pattern versions only use one class. It wouldn’t be worth the extra work, for code as small as this sample.

I would probably only consider using this pattern if there were at least five different ways to perform the strategy’s function. Or, if I could re-use the concrete strategy classes in several places.

 

IAveragingMethod.cs

using System.Collections.Generic;
namespace Engine.StrategyPattern.PatternVersion
{
    public interface IAveragingMethod
    {
        double AverageFor(List<double> values);
    }
}

This is the interface for the strategy.

The interface states that the “concrete strategy” classes (the classes that perform the function) need to have these properties/methods/etc.

For this example, the concrete strategy classes need an “AverageFor” function, which accepts a list of doubles, and returns a double.

 

AverageByMean.cs

using System.Collections.Generic;
using System.Linq;
namespace Engine.StrategyPattern.PatternVersion
{
    public class AverageByMean : IAveragingMethod
    {
        public double AverageFor(List<double> values)
        {
            // Simple method to calculate average: 
            // sum of all values, divided by number of values.
            return values.Sum() / values.Count;
        }
    }
}

The concrete strategy to calculate an average, using the “mean” method.

 

AverageByMedian.cs

using System.Collections.Generic;
using System.Linq;
namespace Engine.StrategyPattern.PatternVersion
{
    public class AverageByMedian : IAveragingMethod
    {
        public double AverageFor(List<double> values)
        {
            // Median average is the middle value of the values in the list.
            var sortedValues = values.OrderBy(x => x).ToList();
            // Use the "%" (modulus) to determine if there is an even, or odd, number of values.
            if(sortedValues.Count % 2 == 1)
            {
                // Number of values is odd.
                // Return the middle value of the sorted list.
                // REMEMBER: The list's index is zero-based, so we subtract 1, instead of adding 1,
                //           to determine which element of the list to return
                return sortedValues[(sortedValues.Count - 1) / 2];
            }
            // Number of values is even.
            // Return the mean average of the two middle values.
            // REMEMBER: The list's index is zero-based, so we subtract 1, instead of adding 1,
            //           to determine which elements of the list to use
            return (sortedValues[(sortedValues.Count / 2) - 1] + 
                sortedValues[sortedValues.Count / 2]) / 2;
        }
    }
}

The concrete strategy to calculate an average, using the “median” method.

 

Calculator.cs

using System.Collections.Generic;
namespace Engine.StrategyPattern.PatternVersion
{
    public class Calculator
    {
        public double CalculateAverageFor(List<double> values, IAveragingMethod averagingMethod)
        {
            return averagingMethod.AverageFor(values);
        }
    }
}

The function to calculate the average receives a list of numbers, and an object that is one of the concrete strategy classes. Because the datatype for the averagingMethod parameter is “IAveragingMethod”, we can pass in any object that implements that interface (matches the requirements for our strategy).

Then, the CalculateAverageFor() method uses the AverageFor() function in the concrete strategy class to perform the calculation.

 

TestCalculator.cs

using System;
using System.Collections.Generic;
using Engine.StrategyPattern.PatternVersion;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace TestEngine.StrategyPattern.PatternVersion
{
    [TestClass]
    public class TestCalculator
    {
        private readonly List<double> _values = new List<double> {10, 5, 7, 15, 13, 12, 8, 7, 4, 2, 9};
        [TestMethod]
        public void Test_AverageByMean()
        {
            Calculator calculator = new Calculator();
            var averageByMean = calculator.CalculateAverageFor(_values, new AverageByMean());
            Assert.IsTrue(ResultsAreCloseEnough(8.3636363, averageByMean));
        }
        [TestMethod]
        public void Test_AverageByMedian()
        {
            Calculator calculator = new Calculator();
            var averageByMedian = calculator.CalculateAverageFor(_values, new AverageByMedian());
            Assert.IsTrue(ResultsAreCloseEnough(8, averageByMedian));
        }
        // Because we are using doubles (floating point values), the values may not exactly match.
        // If the difference between the expected result, and the calculated result is less than .000001,
        // consider the two values as "equal".
        private bool ResultsAreCloseEnough(double expectedResult, double calculatedResult)
        {
            var difference = Math.Abs(expectedResult - calculatedResult);
            return difference < .000001;
        }
    }
}

This is how you would call a method that uses the strategy design pattern.

You pass in the list of numbers to average, and an instance of the appropriate concrete strategy class.

 

Where I’ve found it useful

The most common place for me to use this design pattern is when I need to validate a business object, but have several different ways it might need to be validated. This pattern lets me call a single “Validate” function that accepts the business object and the appropriate concrete strategy validator object.

This design pattern, and the Command Design Pattern, are examples of dependency inversion. Dependency inversion is a technique that can make your code easier to modify, and easier to test.

This is also a technique you can use to implement “composition over inheritance” – another pattern I’ll demonstrate soon.

 

All my design pattern lessons

Source code for my design pattern lessons

 

10 Comments

  1. MarkR
    MarkR October 4, 2016

    Hey. Been enjoying your design pattern videos a great deal. Thanks. Small suggestion about the volume, which is pretty low in a few cases. You can increase or decrease an upload’s volume after the fact. See <a href=”https://www.youtube.com/watch?v=dvxTWXVMikg” target=”_blank”>this video</a>.

    On topic of pattern use. I’m working on a text adventure that will be running in WPF. I’m looking for <a href=”http://stackoverflow.com/questions/3845458/coding-interactions-in-a-text-adventure” target=”_blank”>this type</a> interaction behavior, but I’m having a hard time deciphering from that post how to implement this sort of thing. Is there a pattern that might be handy in this situation? I’m currently looking into the Visitor pattern, but I haven’t researched it far enough to know if it’s going to be useful (same with the Strategy pattern). I’d like to avoid code repetition as much as possible and keep things neat — my previous attempt at a text adventure (running in the console), while functional, was a jumbled mess, full of switch statements for every conceivable player input (didn’t use a parser at the time). This was before I came across your RPG tutorial, which, among other things, taught me sensible code structure.

    So far I can match an interaction to the correct response, but I’m reluctant to scale up in fear of going down the wrong path.

    A side note.
    The way I’m matching interactions with responses right now is, every item has a set of states (enum), as do the responses (each response also targets a specific item). If the currentItem’s states and ID match any response, you get the appropriate response. Probably not ideal, but it’s working for the moment.

    <code>
    Items.Add(new Item(ITEM_ID_APT_BEER, “apartment beer”, BeerVerbs, BeerSpelling,
    canReach: State.Accessible,
    canSee: State.Visible,
    canTake: State.Collectable,
    canDrink: State.Drinkable));
    </code>
    (all states are optional; also I didn’t want the parameter order to be an issue, so I’m using parameter names in the constructor)
    (apt is short for apartment; the beer is in the fridge; for testing purposes it is accessible, otherwise it’s inaccessible until the player opens the fridge)

    <code>
    Responses.Add(new Response(APT_BEER_FIRDGE_PICKUP_POS_RESPONSE, “You pick up the beer can. Your fridge is now one beer can short.”, ITEM_ID_APT_BEER,
    canReach: State.Accessible,
    canTake: State.Collectable));

    foreach (State state in States.GetValues<State>())
    {
    Console.WriteLine(state);
    foreach (Response r in Responses)
    if ((r.TargetID == currentItem.ID) && (r.CanDrink == state) && (currentItem.CanDrink == state))
    {
    Console.WriteLine(r.Content);
    break;
    }
    }
    </code>

    • Scott Lilly
      Scott Lilly October 6, 2016

      Can you give me a couple days to think about this?

      My first instinct is to build the state machine (which, I assume, is in the Item base class) so it lets you define the available “action” and “response” combinations it understands. With a function like:
      public void AddActionResponse(Action action, Response response). I think this will be cleaner than adding more parameters to the constructor, as you add more functionality to the game.

      That pair is added to a Dictionary variable inside the state machine. If the player tries to “take the house”, the state machine would know that is not possible – because there is no “Take” action in its dictionary. Or, you could initialize it so the response to a “Take” action, for a house object, is to display a message “You aren’t that strong!”

      But, I’d like to think a bit more about this, to consider if there are problems with this solution, or if there is a better way to do this.

  2. ed
    ed December 9, 2017

    I spend ages searching the web for a series that I can devote a few days study to.
    Rare.
    Brilliant.

  3. Arctur Gray
    Arctur Gray February 25, 2018

    If strategy includes only one “step”, I think it is easier and cleaner to use a delegate instead of an interface. Create one static class Average or whatever and give it different static methods for different strategies. Then create a delegate of an appropriate type, and we are good to go.

    • Scott Lilly
      Scott Lilly February 25, 2018

      Yes, in a real program, you would probably calculate averages differently. Normally, you would use the Strategy pattern for more complex logic. Average calculation was just simple logic that could be used to demonstrate the pattern.

  4. Ken Palmer
    Ken Palmer June 24, 2019

    Scott, thank you for this very clear and concise example.

  5. Simon
    Simon July 14, 2021

    Just wanted to say that your videos are super valuable, relevant, easy to follow and easy to understand!

    • Scott Lilly
      Scott Lilly July 14, 2021

      Thank you, Simon.

      I’m currently preparing to update them for .NET 5, add more design patterns, and re-do the videos (to fix the sound).

Leave a Reply

Your email address will not be published. Required fields are marked *