Strategy¶
- Strategy
- UML class diagram
- Participants
- Structural code in C# ..{10}
- Real-world code in C# .{10}
- .NET Optimized code in C# .{10}
Summary: Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
Frequency of use: Medium high
UML class diagram¶
Participants¶
The classes and objects participating in this pattern are:
- Strategy (
SortStrategy
) - declares an interface common to all supported algorithms. Context uses this interface to call the algorithm defined by a ConcreteStrategy
- ConcreteStrategy (
QuickSort, ShellSort, MergeSort
) - implements the algorithm using the Strategy interface
- Context (
SortedList
) - is configured with a ConcreteStrategy object
- maintains a reference to a Strategy object
- may define an interface that lets Strategy access its data.
Structural code in C# ..{10}¶
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 DoFactory.GangOfFour.Strategy.Structural
{
/// <summary>
/// MainApp startup class for Structural
/// Strategy Design Pattern.
/// </summary>
class MainApp
{
/// <summary>
/// Entry point into console application.
/// </summary>
static void Main()
{
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>
abstract class Strategy
{
public abstract void AlgorithmInterface();
}
/// <summary>
/// A 'ConcreteStrategy' class
/// </summary>
class ConcreteStrategyA : Strategy
{
public override void AlgorithmInterface()
{
Console.WriteLine(
"Called ConcreteStrategyA.AlgorithmInterface()");
}
}
/// <summary>
/// A 'ConcreteStrategy' class
/// </summary>
class ConcreteStrategyB : Strategy
{
public override void AlgorithmInterface()
{
Console.WriteLine(
"Called ConcreteStrategyB.AlgorithmInterface()");
}
}
/// <summary>
/// A 'ConcreteStrategy' class
/// </summary>
class ConcreteStrategyC : Strategy
{
public override void AlgorithmInterface()
{
Console.WriteLine(
"Called ConcreteStrategyC.AlgorithmInterface()");
}
}
/// <summary>
/// The 'Context' class
/// </summary>
class Context
{
private Strategy _strategy;
// Constructor
public Context(Strategy strategy)
{
this._strategy = strategy;
}
public void ContextInterface()
{
_strategy.AlgorithmInterface();
}
}
}
Output¶
Called ConcreteStrategyA.AlgorithmInterface()
Called ConcreteStrategyB.AlgorithmInterface()
Called ConcreteStrategyC.AlgorithmInterface()
Real-world code in C# .{10}¶
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 DoFactory.GangOfFour.Strategy.RealWorld
{
/// <summary>
/// MainApp startup class for Real-World
/// Strategy Design Pattern.
/// </summary>
class MainApp
{
/// <summary>
/// Entry point into console application.
/// </summary>
static void Main()
{
// 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>
abstract class SortStrategy
{
public abstract void Sort(List<string> list);
}
/// <summary>
/// A 'ConcreteStrategy' class
/// </summary>
class QuickSort : SortStrategy
{
public override void Sort(List<string> list)
{
list.Sort(); // Default is Quicksort
Console.WriteLine("QuickSorted list ");
}
}
/// <summary>
/// A 'ConcreteStrategy' class
/// </summary>
class ShellSort : SortStrategy
{
public override void Sort(List<string> list)
{
//list.ShellSort(); not-implemented
Console.WriteLine("ShellSorted list ");
}
}
/// <summary>
/// A 'ConcreteStrategy' class
/// </summary>
class MergeSort : SortStrategy
{
public override void Sort(List<string> list)
{
//list.MergeSort(); not-implemented
Console.WriteLine("MergeSorted list ");
}
}
/// <summary>
/// The 'Context' class
/// </summary>
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();
}
}
}
Output¶
QuickSorted list
Anna
Jimmy
Samual
Sandra
Vivek
ShellSorted list
Anna
Jimmy
Samual
Sandra
Vivek
MergeSorted list
Anna
Jimmy
Samual
Sandra
Vivek
.NET Optimized code in C# .{10}¶
using System;
using System.Collections.Generic;
namespace DoFactory.GangOfFour.Strategy.NETOptimized
{
/// <summary>
/// MainApp startup class for .NET optimized
/// Strategy Design Pattern.
/// </summary>
class MainApp
{
/// <summary>
/// Entry point into console application.
/// </summary>
static void Main()
{
// Two contexts following different strategies
var studentRecords = new SortedList()
{
new Student{ Name = "Samual", Ssn = "154-33-2009" },
new Student{ Name = "Jimmy", Ssn = "487-43-1665" },
new Student{ Name = "Sandra", Ssn = "655-00-2944" },
new Student{ Name = "Vivek", Ssn = "133-98-8399" },
new Student{ Name = "Anna", Ssn = "760-94-9844" },
};
studentRecords.SortStrategy = new QuickSort();
studentRecords.SortStudents();
studentRecords.SortStrategy = new ShellSort();
studentRecords.SortStudents();
studentRecords.SortStrategy = new MergeSort();
studentRecords.SortStudents();
// Wait for user
Console.ReadKey();
}
}
/// <summary>
/// The 'Strategy' interface
/// </summary>
interface ISortStrategy
{
void Sort(List<Student> list);
}
/// <summary>
/// A 'ConcreteStrategy' class
/// </summary>
class QuickSort : ISortStrategy
{
public void Sort(List<Student> list)
{
// Call overloaded Sort
Sort(list, 0, list.Count - 1);
Console.WriteLine("QuickSorted list ");
}
// Recursively sort
private 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 void Swap(List<Student> list, int left, int right)
{
var temp = list[right];
list[right] = list[left];
list[left] = temp;
}
}
/// <summary>
/// A 'ConcreteStrategy' class
/// </summary>
class ShellSort : ISortStrategy
{
public void Sort(List<Student> list)
{
// ShellSort(); not-implemented
Console.WriteLine("ShellSorted list ");
}
}
/// <summary>
/// A 'ConcreteStrategy' class
/// </summary>
class MergeSort : ISortStrategy
{
public void Sort(List<Student> list)
{
// MergeSort(); not-implemented
Console.WriteLine("MergeSorted list ");
}
}
/// <summary>
/// The 'Context' class
/// </summary>
class SortedList : List<Student>
{
// Sets sort strategy
public ISortStrategy SortStrategy { get; set; }
// Perform sort
public void SortStudents()
{
SortStrategy.Sort(this);
// Display sort results
foreach (var student in this)
{
Console.WriteLine(" " + student.Name);
}
Console.WriteLine();
}
}
/// <summary>
/// Represents a student
/// </summary>
class Student
{
// Gets or sets student name
public string Name { get; set; }
// Gets or sets student social security number
public string Ssn { get; set; }
}
}