🤔 What is the Strategy Pattern?

The Strategy Pattern is a behavioral design pattern that defines a family of algorithms, encapsulates each one, and makes them interchangeable. This enables the selection of an algorithm at runtime based on the specific context.

In simple terms, the Strategy Pattern allows you to dynamically change the behavior of an object without modifying its client code.


🧠 Real-Life Analogy

Think of ordering food online. You have various payment options:

  • Credit Card 🏦
  • PayPal 💳
  • Cash on Delivery 💵

You don't need to alter the food ordering process itself; you simply switch between payment methods.


✅ When to Use

  • When you have several ways to perform a similar task and want the client to choose one at runtime.
  • When you want to eliminate complex conditional logic (like if-else or switch-case).
  • When you need interchangeable algorithms.

🏗 Example Use Case: Sorting Strategies

Consider a Sorting System where the algorithm can be Bubble Sort, Quick Sort, or Merge Sort.


🛠️ Java Implementation of Strategy Pattern


✅ 1. Define the Strategy Interface

public interface SortStrategy {
    void sort(int[] array);
}

✅ 2. Create Concrete Strategies

🌀 Bubble Sort

public class BubbleSortStrategy implements SortStrategy {
    @Override
    public void sort(int[] array) {
        System.out.println("Using Bubble Sort...");
        for (int i = 0; i < array.length; i++) {
            for (int j = 0; j < array.length - 1 - i; j++) {
                if (array[j] > array[j + 1]) {
                    // Swap
                    int temp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = temp;
                }
            }
        }
    }
}

🚀 Quick Sort

public class QuickSortStrategy implements SortStrategy {
    @Override
    public void sort(int[] array) {
        System.out.println("Using Quick Sort...");
        quickSort(array, 0, array.length - 1);
    }

    private void quickSort(int[] array, int low, int high) {
        if (low < high) {
            int pivotIndex = partition(array, low, high);
            quickSort(array, low, pivotIndex - 1);
            quickSort(array, pivotIndex + 1, high);
        }
    }

    private int partition(int[] array, int low, int high) {
        int pivot = array[high];
        int i = (low - 1);
        for (int j = low; j < high; j++) {
            if (array[j] <= pivot) {
                i++;
                int temp = array[i];
                array[i] = array[j];
                array[j] = temp;
            }
        }
        int temp = array[i + 1];
        array[i + 1] = array[high];
        array[high] = temp;
        return i + 1;
    }
}

🌐 Merge Sort

public class MergeSortStrategy implements SortStrategy {
    @Override
    public void sort(int[] array) {
        System.out.println("Using Merge Sort...");
        mergeSort(array, 0, array.length - 1);
    }

    private void mergeSort(int[] array, int left, int right) {
        if (left < right) {
            int mid = (left + right) / 2;
            mergeSort(array, left, mid);
            mergeSort(array, mid + 1, right);
            merge(array, left, mid, right);
        }
    }

    private void merge(int[] array, int left, int mid, int right) {
        int[] leftArray = new int[mid - left + 1];
        int[] rightArray = new int[right - mid];

        System.arraycopy(array, left, leftArray, 0, mid - left + 1);
        System.arraycopy(array, mid + 1, rightArray, 0, right - mid);

        int i = 0, j = 0;
        int k = left;
        while (i < leftArray.length && j < rightArray.length) {
            if (leftArray[i] <= rightArray[j]) {
                array[k] = leftArray[i];
                i++;
            } else {
                array[k] = rightArray[j];
                j++;
            }
            k++;
        }

        while (i < leftArray.length) {
            array[k] = leftArray[i];
            i++;
            k++;
        }

        while (j < rightArray.length) {
            array[k] = rightArray[j];
            j++;
            k++;
        }
    }
}

✅ 3. Context Class

public class SorterContext {
    private SortStrategy sortStrategy;

    public SorterContext(SortStrategy sortStrategy) {
        this.sortStrategy = sortStrategy;
    }

    public void setSortStrategy(SortStrategy sortStrategy) {
        this.sortStrategy = sortStrategy;
    }

    public void executeSort(int[] array) {
        sortStrategy.sort(array);
    }
}

✅ 4. Client Code

public class StrategyPatternDemo {
    public static void main(String[] args) {
        int[] array = {5, 3, 8, 1, 2};

        SorterContext context = new SorterContext(new BubbleSortStrategy());
        context.executeSort(array);  // Using Bubble Sort...

        System.out.println("Array after Bubble Sort: " + Arrays.toString(array));

        // Switching strategy to QuickSort
        context.setSortStrategy(new QuickSortStrategy());
        context.executeSort(array);  // Using Quick Sort...

        System.out.println("Array after Quick Sort: " + Arrays.toString(array));

        // Switching strategy to MergeSort
        context.setSortStrategy(new MergeSortStrategy());
        context.executeSort(array);  // Using Merge Sort...

        System.out.println("Array after Merge Sort: " + Arrays.toString(array));
    }
}

🧪 Output

Using Bubble Sort...
Array after Bubble Sort: [1, 2, 3, 5, 8]
Using Quick Sort...
Array after Quick Sort: [1, 2, 3, 5, 8]
Using Merge Sort...
Array after Merge Sort: [1, 2, 3, 5, 8]

📦 Structure Recap

+---------------------+    +-----------------------+  
|     SorterContext   |<>--|    SortStrategy       |  
+---------------------+    +-----------------------+  
| - sortStrategy      |    | + sort(array)         |  
| + setSortStrategy() |    +-----------------------+  
| + executeSort()     |            ^  
+---------------------+            |  
                 +---------------------+  
                 | BubbleSortStrategy  |  
                 +---------------------+  
                 | + sort(array)       |  
                 +---------------------+  
                           ^  
                           |  
                   +---------------------+  
                   | QuickSortStrategy   |  
                   +---------------------+  
                   | + sort(array)       |  
                   +---------------------+  
                           ^  
                           |  
                   +---------------------+  
                   | MergeSortStrategy   |  
                   +---------------------+  
                   | + sort(array)       |  
                   +---------------------+

🧠 Key Benefits

Benefit Explanation
Flexibility Dynamically select algorithms or strategies
Open/Closed Principle Add new strategies without changing existing code
Reusability Common functionality shared among different algorithms
Easy to Understand Avoids complex conditional chains for selecting strategies

🛠️ Real Use Cases

  • Sorting Algorithms (BubbleSort, QuickSort, MergeSort)
  • Payment Methods (Credit, Debit, PayPal)
  • Navigation (Car, Bike, Walking routes)
  • Logging (File, Console, Database)

💡 Bonus Tips

  • Combine with the Factory Pattern to dynamically create strategies.
  • Use in situations where algorithm selection happens at runtime.

✅ Summary Table

Element Role
SortStrategy Defines the strategy interface
BubbleSortStrategy, QuickSortStrategy, MergeSortStrategy Concrete strategy classes
SorterContext Manages and switches strategies
Client Invokes strategy methods

🚀 Wrapping Up Day 10!

This concludes the final day of our Design Patterns Series! 🎉 We’ve explored 10 key design patterns, complete with code samples, real-life examples, and explanations. 💻

You're now prepared to tackle design pattern questions in interviews and apply them in your coding projects.

Feel free to revisit this series whenever needed. Good luck with your coding journey, and let me know if you're ready to dive into more advanced topics! 🚀💯