If there was one all-powerful methodology to, say, managing a software project, then there wouldn't be a need to learn any of the others. The reality of project management though, as is true in much of the development industry, is knowing how to make compromises; depending on the situation, one approach might be more efficient than another. When you need to vary the strategy a program will use from one situation to the next, the strategy pattern is an obvious choice.

Assumptions

What is the Strategy Pattern?

The strategy pattern is a behavioral design pattern that allows a class to use different algorithms at runtime.

<div style="width:100%; margin:auto;text-align:center;"><img src="https://www.devmaking.com/img/topics/designpatterns/StrategyPattern_01.png" alt="strategy pattern UML diagram" style="max-width:95%;"> </div>

A popular example of strategy design is being able to switch sorting algorithms on the fly: if list A is known to be mostly sorted, you might consider using one algorithm over another. Alternatively, if list B is know to be completely random, a different algorithm might be more efficient.

Conceptualization

To best demonstrate the effectiveness of the strategy pattern, consider the following code; our class ArraySorter attempts to sort using different algorithms, however all of the algorithms are stored in a single file. The sorting method that will be used is decided by a string input:

Before the strategy pattern:

class ArraySorter {
   String sortingMethod;
   
   void setMethod(String method) {
     this.sortingMethod = method;
   }
   
   // Sort an array based on the current sorting method:
   void sort(int[] arr) {
     if(sortingMethod == "insertionSort") {
       // Do insertion sort:
       for ( int i = 0; i &lt; arr.length; i++ ) {
         int tmp = arr[i];
         int j = i - 1;
         while ( j &gt;= 0 &amp;&amp; arr[j] &gt; tmp ) {
             arr[j + 1] = arr[j];
             j = j - 1;
         }
         arr[j + 1] = tmp;
           }
     }
     else if(sortingMethod == "selectionSort") {
       // Do selection sort:
       for ( int i =0; i &lt; arr.length - 1; i++ ){
         int min = i;
         for ( int j = i + 1; j &lt; arr.length; j++ ){
             if ( arr[j] &lt; arr[min] ){
                 min = j;
             }
         }
         int tmp = arr[min];
         arr[min] = arr[i];
         arr[i] = tmp;
           }
     }
     // and so on...
   }
 }

Not only is this code bulky and difficult to understand, it would be a nightmare to maintain and extend with new sorting algorithms. Instead, we're going to utilize the following strategy pattern to give our code proper organization:

<div style="width:100%; margin:auto;text-align:center;"><img src="https://www.devmaking.com/img/topics/designpatterns/StrategyPattern_02.png" alt="array sorter strategy pattern UML diagram" style="max-width:95%;"> </div>

Using an interface SortingStrategy, our different algorithms will sort in their own classes implementing the interface. These classed can be called upon by the client and used in ArraySorter dynamically.

After implementing Strategy

// Strategy interface:
interface SortingStrategy {
   void sort(int[] arr);
 }

// Concrete strategy 1:
class InsertionStrategy implements SortingStrategy {
   void sort(int[] arr) {
     // Do insertion sort:
       for ( int i = 0; i &lt; arr.length; i++ ) {
         int tmp = arr[i];
         int j = i - 1;
         while ( j &gt;= 0 &amp;&amp; arr[j] &gt; tmp ) {
             arr[j + 1] = arr[j];
             j = j - 1;
         }
         arr[j + 1] = tmp;
           }
   }
   
 }
// Concrete strategy 2:
class SelectionStrategy implements SortingStrategy {
   void sort(int[] arr) {
     // Do selection sort:
       for ( int i =0; i &lt; arr.length - 1; i++ ){
         int min = i;
         for ( int j = i + 1; j &lt; arr.length; j++ ){
             if ( arr[j] &lt; arr[min] ){
                 min = j;
             }
         }
         int tmp = arr[min];
         arr[min] = arr[i];
         arr[i] = tmp;
           }
     }
   }

 }

// Context class:
class ArraySorter {
   // maintain a reference to a strategy:
   SortingStrategy strategy;
   
   ArraySorter() {
     // Optional: set a default strategy in the constructor.
     strategy = new InsertionStrategy();
   }
   
   // Set the strategy:
   void setStrategy(SortingStrategy strategy) {
     this.strategy = strategy;
   }
   
   // Executes the strategy:
   void sort(int[] arr) {
     strategy.sort(arr);
   }
 }    

Now our code is nice and organized! Additionally, if we wanted to implement new algorithms, we'd only need to define a new class implementing the SortingStrategy interface and it will work immediately in our application. This helps keep our code maintainable and reusable.

Client Code:

static void main(String[] args) {
      // array to sort:
   int[] array = {8, 2, 65, 1, 97, 123, 6, 82, 4};
   
   ArraySorter sorter = new ArraySorter();
   
   String sortingMethod = args[0];
   
   if(sortingMethod == "insertionSort") {
     sorter.setStrategy(new InsertionStrategy());
   }
   else if(sortingMethod == "selectionSort") {
     sorter.setStrategy(new SelectionStrategy());
   }
   // and so on...
   else {
     throw new Exception("Unrecognized sorting strategy!");
   }
   // sort the array:
   sorter.sort(array);
   
   String res;
   for(i : array) {
     res += i + " ";
   }
   print(res);
   // Done!
 }

> Challenge: in you language of choice, extend the example with MergeSort and QuickSort!

Output:

1 2 4 6 8 65 82 123