The Strategy design pattern defines a family of algorithms, encapsulate each one, and make them interchangeable. This pattern lets the algorithm vary independently from clients that use it.
C# code examples of the Strategy design pattern is provided in 3 forms:
A visualization of the classes and objects participating in this pattern.
The classes and objects participating in this pattern include:
SortStrategy
)
QuickSort, ShellSort, MergeSort
)
SortedList
)
This structural code demonstrates the Strategy pattern which encapsulates functionality in the form of an object. This allows clients to dynamically change algorithmic strategies.
using System;
namespace Strategy.Structural
{
/// <summary>
/// Strategy Design Pattern
/// </summary>
public class Program
{
public static void Main(string[] args)
{
Context context;
// Three contexts following different strategies
context = new Context(new ConcreteStrategyA());
context.ContextInterface();
context = new Context(new ConcreteStrategyB());
context.ContextInterface();
context = new Context(new ConcreteStrategyC());
context.ContextInterface();
// Wait for user
Console.ReadKey();
}
}
/// <summary>
/// The 'Strategy' abstract class
/// </summary>
public abstract class Strategy
{
public abstract void AlgorithmInterface();
}
/// <summary>
/// A 'ConcreteStrategy' class
/// </summary>
public class ConcreteStrategyA : Strategy
{
public override void AlgorithmInterface()
{
Console.WriteLine(
"Called ConcreteStrategyA.AlgorithmInterface()");
}
}
/// <summary>
/// A 'ConcreteStrategy' class
/// </summary>
public class ConcreteStrategyB : Strategy
{
public override void AlgorithmInterface()
{
Console.WriteLine(
"Called ConcreteStrategyB.AlgorithmInterface()");
}
}
/// <summary>
/// A 'ConcreteStrategy' class
/// </summary>
public class ConcreteStrategyC : Strategy
{
public override void AlgorithmInterface()
{
Console.WriteLine(
"Called ConcreteStrategyC.AlgorithmInterface()");
}
}
/// <summary>
/// The 'Context' class
/// </summary>
public class Context
{
Strategy strategy;
// Constructor
public Context(Strategy strategy)
{
this.strategy = strategy;
}
public void ContextInterface()
{
strategy.AlgorithmInterface();
}
}
}
This real-world code demonstrates the Strategy pattern which encapsulates sorting algorithms in the form of sorting objects. This allows clients to dynamically change sorting strategies including Quicksort, Shellsort, and Mergesort.
using System;
using System.Collections.Generic;
namespace Strategy.RealWorld
{
/// <summary>
/// Strategy Design Pattern
/// </summary>
public class Program
{
public static void Main(string[] args)
{
// Two contexts following different strategies
SortedList studentRecords = new SortedList();
studentRecords.Add("Samual");
studentRecords.Add("Jimmy");
studentRecords.Add("Sandra");
studentRecords.Add("Vivek");
studentRecords.Add("Anna");
studentRecords.SetSortStrategy(new QuickSort());
studentRecords.Sort();
studentRecords.SetSortStrategy(new ShellSort());
studentRecords.Sort();
studentRecords.SetSortStrategy(new MergeSort());
studentRecords.Sort();
// Wait for user
Console.ReadKey();
}
}
/// <summary>
/// The 'Strategy' abstract class
/// </summary>
public abstract class SortStrategy
{
public abstract void Sort(List<string> list);
}
/// <summary>
/// A 'ConcreteStrategy' class
/// </summary>
public class QuickSort : SortStrategy
{
public override void Sort(List<string> list)
{
list.Sort(); // Default is Quicksort
Console.WriteLine("QuickSorted list ");
}
}
/// <summary>
/// A 'ConcreteStrategy' class
/// </summary>
public class ShellSort : SortStrategy
{
public override void Sort(List<string> list)
{
//list.ShellSort(); not-implemented
Console.WriteLine("ShellSorted list ");
}
}
/// <summary>
/// A 'ConcreteStrategy' class
/// </summary>
public class MergeSort : SortStrategy
{
public override void Sort(List<string> list)
{
//list.MergeSort(); not-implemented
Console.WriteLine("MergeSorted list ");
}
}
/// <summary>
/// The 'Context' class
/// </summary>
public class SortedList
{
private List<string> list = new List<string>();
private SortStrategy sortstrategy;
public void SetSortStrategy(SortStrategy sortstrategy)
{
this.sortstrategy = sortstrategy;
}
public void Add(string name)
{
list.Add(name);
}
public void Sort()
{
sortstrategy.Sort(list);
// Iterate over list and display results
foreach (string name in list)
{
Console.WriteLine(" " + name);
}
Console.WriteLine();
}
}
}
The .NET optimized code demonstrates the same code as above but uses
more modern C# and .NET features.
Here is an elegant C# Strategy solution.
namespace Strategy.NetOptimized;
using static System.Console;
using System;
using System.Collections.Generic;
/// <summary>
/// Strategy Design Pattern
/// </summary>
public class Program
{
public static void Main()
{
// Two contexts following different strategies
SortedList students =
[
new (Name:"Samual", Ssn: "154-33-2009"),
new (Name:"Jimmy", Ssn: "487-43-1665" ),
new (Name:"Sandra", Ssn: "655-00-2944" ),
new (Name:"Vivek", Ssn: "133-98-8399" ),
new (Name:"Anna", Ssn: "760-94-9844" )
];
students.SortStrategy = new QuickSort();
students.SortStudents();
students.SortStrategy = new ShellSort();
students.SortStudents();
students.SortStrategy = new MergeSort();
students.SortStudents();
// Wait for user
ReadKey();
}
}
/// <summary>
/// The 'Strategy' interface
/// </summary>
public interface ISortStrategy
{
void Sort(List<Student> list);
}
/// <summary>
/// A 'ConcreteStrategy' class
/// </summary>
public class QuickSort : ISortStrategy
{
public void Sort(List<Student> list)
{
// Call overloaded Sort
Sort(list, 0, list.Count - 1);
WriteLine("QuickSorted list ");
}
// Recursively sort
private static void Sort(List<Student> list, int left, int right)
{
int lhold = left;
int rhold = right;
// Use a random pivot
var random = new Random();
int pivot = random.Next(left, right);
Swap(list, pivot, left);
pivot = left;
left++;
while (right >= left)
{
int compareleft = list[left].Name.CompareTo(list[pivot].Name);
int compareright = list[right].Name.CompareTo(list[pivot].Name);
if ((compareleft >= 0) && (compareright < 0))
{
Swap(list, left, right);
}
else
{
if (compareleft >= 0)
{
right--;
}
else
{
if (compareright < 0)
{
left++;
}
else
{
right--;
left++;
}
}
}
}
Swap(list, pivot, right);
pivot = right;
if (pivot > lhold) Sort(list, lhold, pivot);
if (rhold > pivot + 1) Sort(list, pivot + 1, rhold);
}
// Swap helper function
private static void Swap(List<Student> list, int left, int right)
{
// Tuple based swap
(list[left], list[right]) = (list[right], list[left]);
}
}
/// <summary>
/// A 'ConcreteStrategy' class
/// </summary>
public class ShellSort : ISortStrategy
{
public void Sort(List<Student> list)
{
// ShellSort(); not-implemented
WriteLine("ShellSorted list ");
}
}
/// <summary>
/// A 'ConcreteStrategy' class
/// </summary>
public class MergeSort : ISortStrategy
{
public void Sort(List<Student> list)
{
// MergeSort(); not-implemented
WriteLine("MergeSorted list ");
}
}
/// <summary>
/// The 'Context' class
/// </summary>
public class SortedList : List<Student>
{
// Sets sort strategy
public ISortStrategy SortStrategy { get; set; } = null!;
// Perform sort
public void SortStudents()
{
SortStrategy.Sort(this);
// Display sort results
foreach (var student in this)
{
WriteLine($" {student.Name}");
}
WriteLine();
}
}
/// <summary>
/// Represents a student
/// </summary>
public record Student (string Name, string Ssn);