5 min. read

The strategy pattern

A simple yet effective pattern to control what behavior to execute.

We’ll start with code and then I want to empathize the importance of this pattern.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
using System.Collections.Generic;

namespace Donuts
{

public class Donut
{
public int SweetnessLevel { get; protected set; }
}

public class ChocolateDonut : Donut
{
public ChocolateDonut() { SweetnessLevel = 6; }
}
public class BakedJellyDonut : Donut
{
public BakedJellyDonut() { SweetnessLevel = 4; }
}

public interface IDonutMaker
{
Donut Make();
}
public class ChocolateDonutMaker : IDonutMaker
{
public Donut Make()
{
// .. Making the donut
return new ChocolateDonut();
}
}
public class BakedJellyDonutMaker : IDonutMaker
{
public Donut Make()
{
// .. Making the donut
return new BakedJellyDonut();
}
}

public class Program
{
public static void Main(string[] args)
{
// Dictionary is like a HashMap in Java or an std::map in C++.
IDictionary<string, IDonutMaker> donutMakers = new Dictionary<string, IDonutMaker>()
{
{ "Chocolate" , new ChocolateDonutMaker() },
{ "BakedJelly", new BakedJellyDonutMaker() }
};

var donutRecipe = args[0];
var donut = donutMakers[donutRecipe].Make();

// Give customer donut
}
}
}

The IDonutMaker interface

We’ve introduced an interface and not a concrete class.
This is the first important lesson - Code to interfaces and not classes.

Actually CSharps interface is the implementation of the strategy pattern.
Therefore it’s the easier choice for creating an abstracted logic.

In C++ I prefer to create abstract classes or virtual classes to create a base logic.
C# has the keyword ‘interface’ therefore it makes a convention of naming them “IXxxYyy”.
In C++ I prefer to abandon it as it makes less sense although it’s based on preferences and company policies.

1
2
3
4
5
6
7
8
9
struct Donut 
{
};

class DonutMaker
{
public:
virtual Donut Make() = 0;
};

The Strategy pattern - A composable method to choose behavior on the fly

The pattern allows one to change the behavior very quickly based on choices or other conditions.
Usually it works well with a dictionay such as the C# Code creates.
In this example - based on the recipe we choose the appropriate donut maker behavior.

This pattern allows us to compose our classes in a decoupled way.
Composition is better than inheritance because we can remove or add additional behavior based on what we need.
If we try to make more inheritance hirarchies it creates more mess than we intended:

  • C# Doesn’t allow multiple class inheritance (as of C# 9 it allows with interfaces), therefore I implemented it using C++.
    1
    2
    3
    4
    5
    6
    class ChocolateDonutMaker : public DonutMaker{ /* omitted for clarity */ };

    class BakedJellyDonutMaker : public DonutMaker { /* omitted for clarity*/ };

    class DonutsMaker : public ChocolateDonutMaker , public BakedJellyDonutMaker
    { /* Omitted for clarity */ };

More donut makers will bloat this class into a god-class where it must implement all of the logic.
It’s bug prone and leads to very messy code.

Bad code:

1
2
3
class DonutsMaker : public ChocolateDonutMaker , public BakedJellyDonutMaker,
public BananaDonutMaker, public PeanutButterDonutMaker // ... and on and on
{ /* Omitted for clarity */ };

Diagram

When speaking of an abstract pattern we could visuallize it as follow:

A consumer that needs an abstracted logic can be composed of the interface.
This is equivilant to holding a pointer or a reference to the interface (Depending on what language you’re programming in).

How the consumer gets the concrete implementation is another topic, just to mention the different ways:

  • Injecting through ctor, property or method.
  • Creating and holding the instances as a list or a dictionary inside the class.
  • Abstracting this behavior into another class -> This usually is an overdesign unless you have too many different strategies.

A solid pattern

Explaining SOLID is yet another topic so I’ll refrain from it in here.
The strategy pattern is a safe way to implement a solid-based code.

It encourages us to create more meaningful interfaces with much lower coupeling and higher cohesion.
Also we depend on the interface and not the concrete implementations making the code easier to extend and maintain.

Testable

The pattern encourages us to split different behaviors to different implementations, making it easier to unit test individual classes.

This is C# unit test code written using NUnit framework.

1
2
3
4
5
6
7
8
9
10
11
 [Test]
public void ChocolateDonutMaker_Make_MakesChocolateSweet()
{
const int MinimalSweetnessLevel = 5;

ChocolateDonutMaker maker = new ChocolateDonutMaker();
var donut = maker.make();

// We want chocolate donuts to be sweet!
Assert.That(donut.SweetnessLevel, Is.GreaterThan(MinimalSweetnessLevel));
}

The strategy pattern is very basic yet knowing all of the avdantages will help us be more conscious about what kind of code we’re writing, and in the end will help us produce higher quality code.

Thanks for reading!


Is this helpful? You can show your appreciation here: Buy me a coffee