Design Patterns Simplified - Strategy

This article explains what Strategy Design Pattern is and how to use it in software design and development.

I am here to discuss one of the popular behavioral design patterns, called Strategy. Before going through its implementation, let’s begin by defining it.

As per GOF guys, Strategy Pattern is defined as following.

Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.

Well! Let’s understand what they mean and where this pattern can be fit.

It simply means that this pattern is about having a set of concrete strategies or family of algorithms and choosing the appropriate one at runtime based on the need. Some of the use cases can be classified.

  • Choosing an appropriate sort algorithm at runtime out of several (Quick sort, Merge sort, Insertion sort etc.).
  • Selecting the right hash algorithm at runtime out of the bunch (MD5, SHA1, SHA256 etc.).
  • Opting a relevant average calculation method (Mean, Median, Mode etc.) at runtime.
  • Picking an applicable billing strategy (Regular, Happy hour etc.) at runtime.
  • Setting a suitable shipping courier object (FedEx, Bluedart, DTDC) at runtime.

Another example of strategy pattern in .NET framework is Sort method (Array.Sort). It uses insertion, heap, and quicksort at runtime based on the incoming data.

Excerpts from MSDN tell,

This method uses the introspective sort (introsort) algorithm as follows,
  • If the partition size is fewer than 16 elements, it uses an insertion sort algorithm.
  • If the number of partitions exceeds 2 * LogN, where N is the range of the input array, it uses a Heapsort algorithm.
  • Otherwise, it uses a Quicksort algorithm.

Now, let’s see the custom implementation of the pattern. Please have a look at the code map diagram of the demo we have created.

Design patterns

As you can see, Strategy pattern has some key components as given below.

  • Strategy: -> Interface/abstract class for all supported algorithms. Here ISortStrategy is created to serve the purpose.
  • ConcreteStrategy -> implements the algorithm defined in Strategy interface. Here QuickSort, HeapSort, and InsertionSort have been created for it.
  • Context -> Sets the right strategy object and configured to use concrete strategy classes. Here, SortContext is doing this job.
  • Client -> Calls the context object to set the context and then call the required method in context to do the job. Client class is serving the need here.

How Strategy pattern works

Let’s understand this by a demo project that we have created. First, create the Strategy interface to start with.

  1. using System.Collections.Generic;  
  2.   
  3. namespace StrategyPatternDemo  
  4. {  
  5.     interface ISortStrategy  
  6.     {  
  7.         void Sort<T>(List<T> data);  
  8.     }  
  9. }  

As you can see, we have created a generic method to support all types of data. Now, let's create a concrete strategy class to implement the interface we have created above.

  1. using System;  
  2. using System.Collections.Generic;  
  3.   
  4. namespace StrategyPatternDemo  
  5. {  
  6.     class HeapSort : ISortStrategy  
  7.     {  
  8.         public void Sort<T>(List<T> list)  
  9.         {  
  10.             list.Sort();  
  11.             //We used default sort (.NET Fx) however you can write custom code as well  
  12.             Console.WriteLine($"After Sort using {this.GetType().Name}:");  
  13.         }  
  14.     }  
  15. }  
  16.   
  17. //Namespace same as above  
  18. class InsertionSort : ISortStrategy  
  19.     {  
  20.         public void Sort<T>(List<T> list)  
  21.         {  
  22.             list.Sort();  
  23.             //We used default sort (.NET Fx) however you can write custom code as well  
  24.             Console.WriteLine($"After Sort using {this.GetType().Name}:");  
  25.         }  
  26.     }  
  27.   
  28. //Namespace same as above  
  29. class QuickSort : ISortStrategy  
  30.     {  
  31.         public void Sort<T>(List<T> list)  
  32.         {  
  33.             list.Sort();  
  34.             //We used default sort (.NET Fx) however you can write custom code as well  
  35.             Console.WriteLine($"After Sort using {this.GetType().Name}:");  
  36.         }  
  37.     }  

As you can see in the above code that we have given unique implementations to each sort strategy, although we have used FCL Sort method in all three implementations as objective here is to explain design pattern instead of sorting algorithm, however, you can write your own code in each of them.

So at this point, we are done with strategy and concrete strategies and that is more than half of the code. Now, let’s create the remaining once and here is the code for Context i.e. SortContext.

  1. using System;  
  2. using System.Collections.Generic;  
  3. using static System.Console;  
  4.   
  5. namespace StrategyPatternDemo  
  6. {    
  7.     class SortContext  
  8.     {  
  9.         private ISortStrategy sortStrategy = null;  
  10.         private void PrintList<T>(List<T> list)  
  11.         {  
  12.             foreach (T name in list)  
  13.             {  
  14.                 Console.Write(name + " ");  
  15.             }  
  16.             WriteLine();  
  17.         }  
  18.   
  19.         public void SetSortStrategy(TypeOfData typeOfData)  
  20.   
  21.         {  
  22.             switch (typeOfData)  
  23.             {  
  24.                 case TypeOfData.DaysInMonth:  
  25.                     this.sortStrategy = new InsertionSort();  
  26.                     break;  
  27.                 case TypeOfData.PassengersInFlight:  
  28.                     this.sortStrategy = new HeapSort();  
  29.                     break;  
  30.                 case TypeOfData.ResidentsInApt:  
  31.                     this.sortStrategy = new QuickSort();  
  32.                     break;  
  33.                 default:  
  34.                     break;  
  35.             }  
  36.         }  
  37.         public void SortAndPrint<T>(List<T> list)  
  38.         {  
  39.             WriteLine("Before Sort:");  
  40.             PrintList(list);  
  41.             sortStrategy.Sort(list);  
  42.             PrintList(list);  
  43.             WriteLine();  
  44.         }         
  45.         public enum TypeOfData  
  46.         {  
  47.             DaysInMonth,  
  48.             PassengersInFlight,  
  49.             ResidentsInApt  
  50.         }  
  51.     }  
  52. }  

As you can see in the class above, i.e., SortContext, we have done a few things as following.

  1. Created an enum to support the type of data to sort.
  2. Setting sort strategy by passing the type of data.
  3. Calling the appropriate Sort method based on the selected strategy in step#2 above.
  4. Calling the reusable Print method to print before and after sort data.

We are almost done. Now, it’s time to set up the client and see something in action. Let’s put the code for it.

  1. using System.Collections.Generic;  
  2. using static System.Console;  
  3.   
  4. namespace StrategyPatternDemo  
  5. {  
  6.     class Client  
  7.     {  
  8.         static void Main(string[] args)  
  9.         {  
  10.             Title = "Strategy Pattern Demo";  
  11.             SortContext sortContext = new SortContext(); ;  
  12.   
  13.             //Sorting days in month  
  14.             var daysInMonth = new List<int> {5, 1, 17, 27, 12};  
  15.             sortContext.SetSortStrategy(SortContext.TypeOfData.DaysInMonth);  
  16.             sortContext.SortAndPrint(daysInMonth);  
  17.   
  18.             //Sorting Passengers in flight  
  19.             var passInFlight = new List<string> {"Kamlesh","Vandana","Prakash","Aradhana","Anay"};  
  20.             sortContext.SetSortStrategy(SortContext.TypeOfData.PassengersInFlight);  
  21.             sortContext.SortAndPrint(passInFlight);  
  22.   
  23.             //Sorting Residents in Apt  
  24.             var resiInApt = new List<string> {"Ramsagar""Siyalali""Mayank"};  
  25.             sortContext.SetSortStrategy(SortContext.TypeOfData.ResidentsInApt);  
  26.             sortContext.SortAndPrint(resiInApt);  
  27.         }  
  28.     }   
  29. }  

And here goes the output of the demo we have developed.

Design patterns

As you can see in the Client class that we are passing three type of data and based on the data, appropriate type of sorting strategy is selected at runtime.

Conclusion

In the article, we have gone through what Strategy pattern is, when and how to use it. Well, it can be used in places where you want to pick one implementation out of some at runtime. You can also download the attached demo project (StrategyPatternDemo.zip) to go through the full source code referred in the article.

Hope you have liked the article. Look forward to your comments/suggestions.

References

  • https://msdn.microsoft.com/en-us/library/afwbytk2(v=vs.110).aspx
  • http://www.dofactory.com/net/strategy-design-pattern
  • https://en.wikipedia.org/wiki/Strategy_pattern