How to Switch the Algorithms at Runtime with Strategy Pattern in C#
When, Why, and How to use Strategy Pattern in C#. The quick and easy introduction.
The Strategy Pattern
Strategy Pattern or SP enables selecting an algorithm at runtime. Simply said, we choose an algorithm based on the incoming dynamic parameter.
Sounds like a decision statement, right? Switching an algorithm based on parameter value? We are talking about switch
or if-else
statements, correct?
Well, yes. SP is some kind of decision statement. But SP follows rules of Object-oriented programming languages and brings its advantages to the table like reusability and extensibility. It also meets the requirements of the SOLID code.
Advantages of the Strategy Pattern
- It’s easy to switch between different algorithms (strategies) in runtime because you’re using polymorphism in the interfaces.
- Clean code because you avoid decision-statements (not complicated).
- More clean code because you separate the concerns into classes (a class to each Strategy).
- Decoupled and easily testable code.
When to Use the Strategy Pattern
- When you need to use several algorithms with different variations, you need to create a concrete class to implement your algorithm.
- When you see conditional statements around several related algorithms.
- When most of your classes have related behaviors.
Components of SP
The pattern consists of three components; Context, Strategy, and Concrete Strategies.
- Context — Scope, where we are using Strategy. For example,
class PhoneNumber
. - Strategy — Strategy is a common interface for Concrete Strategies. For example,
interface IToStringStrategy
. - Concrete Strategies — Concrete classes that are implementing a Strategy interface. For example,
IrelandToStringStrategy.
Mostly, the Context holds the object of Strategy in field or property. We will apply the Concrete Strategy based on the incoming parameter and then execute the algorithm by invoking the Strategy’s method.
Tutorial
Introduction of Project
Here is the repository with Project. It is a .NET Core console application with simple functionality. It detects whenever you press key ‘C’ for Czech or ‘I’ for Ireland. Depended on the pressed key, the application displays the Czech or Irish format of an exemplary phone number. Console servers as a tool for switching CultureInfo of the application. CultureInfo is mostly driven by an environment like a browser or operating system, but for easier manipulation, I created a simple interactive console.
Strategy
Interface IToStringStrategy
works like the prescription of Concrete Strategies. It contains the definition of the ToString
method receiving phone number value. Implementations of the defined method will convert and format phone number.
public interface IToStringStrategy
{
string ToString(long value);
}
Concrete Strategies
Next are Concrete Strategies of the phone number that are transforming the value into formatted string
type.
For Czechia country is the typical format “+420 XXX XXX XXX”. I googled that one of the Ireland telephone number formats is “+353 X XXXX XXXX”.
Unchanging prefixes are defined as constants in the Prefixes
class.
public static class Prefixes
{
public const string Czech = "+420";
public const string Ireland = "+353";
}
Class CzechiaToStringStrategy
holds the implementation of Czech phone numbers. I am using string interpolation for formatting. The syntax is short, readable, and string interpolation takes care of the conversion from long
type by itself. After a colon, you can simply define where specific characters of converted value should be placed.
public class CzechiaToStringStrategy : IToStringStrategy
{
public string ToString(long value)
{
return $"{Prefixes.Czech} {value:### ### ###}";
}
}
Class IrelandToStringStrategy
implements Ireland's phone number formatting. It is using the same tools to brings the desired result.
public class IrelandTostringStrategy : IToStringStrategy
{
public string ToString(long value)
{
return $"{Prefixes.Ireland} {value:# #### ####}";
}
}
Context of Strategy
Class PhoneNumber
serves as Context. It also contains ResolveStrategyByCulture
method, which returns an instance of Concrete Strategy figured out from the value of CurrentCulture.Name
. My application supports only Ireland or Czech culture, so otherwise, an exception will be thrown.
public class PhoneNumber
{
public long Value { get; set; }
public PhoneNumber(long value)
{
Value = value;
}
public override string ToString()
{
IToStringStrategy toStringStrategy = ResolveStrategyByCulture();
return toStringStrategy.ToString(Value);
}
private IToStringStrategy ResolveStrategyByCulture()
{
if (CultureInfo.CurrentCulture.Name == "cs-CZ")
{
return new CzechiaToStringStrategy();
}
if (CultureInfo.CurrentCulture.Name == "ga-IE")
{
return new IrelandTostringStrategy();
}
throw new Exception("Non existing strategy for culture");
}
}
Program
Last at the podium will be the Program class
with Main
method, which is an entry point of every console application in .NET and .NET Core frameworks.
class Program
{
static void Main(string[] args)
{
Console.WriteLine("\nC for Czech, I for Ireland");
var key = Console.ReadKey();
switch (key.Key)
{
case ConsoleKey.C:
CultureInfo.CurrentCulture = new CultureInfo("cs-CZ");
break;
case ConsoleKey.I:
CultureInfo.CurrentCulture = new CultureInfo("ga-IE");
break;
default:
Main(new []{""});
break;
}
var phoneNumber = new PhoneNumber(123456789);
Console.WriteLine($"\n{phoneNumber}");
}
As you can see, I am reading the incoming keys from keyboards and depend on the value I am setting CultureInfo.CurrentCulture
. Next, the phone number object is initialized and printed on the screen.
I don’t need to call Tostring
method, because I am again using string interpolation, and method is invoked automatically.
Summary
Algorithms of ToString
methods are dynamically switched by the user's action at runtime. You can reach the same goal with decision-statements, but it has its drawbacks.
The shown tutorial is a very simple demonstration of how to implement the strategy pattern in C# language. Its simplicity is also its weakness and it may leave you unconvinced to use the Strategy Pattern. Below I collect a few interesting links as more proof that taking advantage of the Strategy Pattern has its benefits.
Where Further To more exploration of Strategy Pattern, I would recommend the Pluralsight course by Filip Ekberg; “C# Design Patterns: Strategy.”
Take a look also at this blog post about Strategy Pattern and Dependency Injection. I found it very easy to understand.
You can always go back to the roots of Design Patterns by reading a book of Gang of Four; “Design Patterns: Elements of Reusable Object-Oriented Software.”