The Strategy design pattern allows you to define a family of algorithms, encapsulate them in separate classes, and make them interchangeable. It promotes flexibility and avoids code duplication.

General

Think of the Strategy design pattern as having a toolbox with different tools (algorithms) to solve a particular problem. Each tool is neatly organized in its own box (class). Instead of using only one tool for everything, you can pick the best tool for the job from your toolbox. This way, if you need to change the tool later or add a new one, you can do so without needing to change the way you use the tools. It keeps your code flexible and well-organized, making it easier to switch or add tools without causing chaos.

Example without Strategy Pattern:

Imagine a sorting utility that currently supports only one sorting algorithm, like Bubble Sort:

public class SortingUtility {
    public void sort(int[] arr) {
        // Bubble Sort logic
    }
}

Refactored Example using Strategy Pattern:

Apply the Strategy pattern to make sorting algorithms interchangeable:

  1. Create a SortStrategy interface with a sort method.
  2. Implement sorting algorithms as classes that implement the SortStrategy interface.

SortStrategy:

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

BubbleSort

class BubbleSort implements SortStrategy { 
	
	public void sort(int[] arr) {
	 // Bubble Sort logic 
	 }
}

QuickSort

class QuickSort implements SortStrategy { 
	
	public void sort(int[] arr) {
	 // Quick Sort logic 
	 }
}

Sorting Utility

public class SortingUtility { 
	private SortStrategy strategy; 
	
	public SortingUtility(SortStrategy strategy) { 
		this.strategy = strategy; 
	} 
	
	public void setStrategy(SortStrategy strategy) { 
		this.strategy = strategy; 
	} 
	
	public void sort(int[] arr) { 
		strategy.sort(arr); 
	} 
}

Usage:

int[] data = { ... };
SortingUtility utility = new SortingUtility(new BubbleSort());
utility.sort(data);
 
// Change sorting strategy
utility.setStrategy(new QuickSort());
utility.sort(data);

Explanation:

In the refactored code, the Strategy pattern is applied by creating the SortStrategy interface, which defines the contract for sorting algorithms. Concrete sorting algorithms like BubbleSort and QuickSort implement this interface. The SortingUtility class takes a sorting strategy during construction and delegates the sorting responsibility to the chosen strategy. This approach allows you to switch sorting strategies easily without modifying the SortingUtility class, promoting code reusability and maintainability.


 Applicability

  1. Use the Strategy pattern when you want to use different variants of an algorithm within an object and be able to switch from one algorithm to another during runtime.The Strategy pattern lets you indirectly alter the object’s behavior at runtime by associating it with different sub-objects which can perform specific sub-tasks in different ways.

  2. Use the Strategy when you have a lot of similar classes that only differ in the way they execute some behavior. The Strategy pattern lets you extract the varying behavior into a separate class hierarchy and combine the original classes into one, thereby reducing duplicate code.

  3. Use the pattern to isolate the business logic of a class from the implementation details of algorithms that may not be as important in the context of that logic.The Strategy pattern lets you isolate the code, internal data, and dependencies of various algorithms from the rest of the code. Various clients get a simple interface to execute the algorithms and switch them at runtime.

  4. Use the pattern when your class has a massive conditional statement that switches between different variants of the same algorithm.The Strategy pattern lets you do away with such a conditional by extracting all algorithms into separate classes, all of which implement the same interface. The original object delegates execution to one of these objects, instead of implementing all variants of the algorithm.


 How to Implement

  1. In the context class, identify an algorithm that’s prone to frequent changes. It may also be a massive conditional that selects and executes a variant of the same algorithm at runtime.

  2. Declare the strategy interface common to all variants of the algorithm.

  3. One by one, extract all algorithms into their own classes. They should all implement the strategy interface.

  4. In the context class, add a field for storing a reference to a strategy object. Provide a setter for replacing values of that field. The context should work with the strategy object only via the strategy interface. The context may define an interface which lets the strategy access its data.

  5. Clients of the context must associate it with a suitable strategy that matches the way they expect the context to perform its primary job.