Java Design Patterns in Depth: Strategy Pattern

Welcome, Java developers! In today’s post, we’re going to explore one of the behavioral design patterns, the Strategy Pattern. This pattern is a valuable tool for organizing code related to algorithms and provides a way to define a family of algorithms, encapsulate each one, and make them interchangeable.

What is the Strategy Pattern?

The Strategy Pattern allows a client to choose an algorithm from a family of algorithms at runtime. This pattern is particularly useful when you need to use different algorithms or implementations based on the context.

Key Components of Strategy Pattern

  • Context: This is the class that uses a Strategy to perform a specific operation.
  • Strategy Interface: This defines the common interface for all concrete strategies.
  • Concrete Strategies: Implement the Strategy interface with specific algorithm implementations.

Implementing the Strategy Pattern

Let’s implement the Strategy Pattern through a simple example where we are going to create a sorting application. The user can choose different sorting strategies, such as bubble sort or quicksort.

Step 1: Define the Strategy Interface

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

The SortStrategy interface defines a method sort that concrete strategies will implement.

Step 2: Create Concrete Strategies

public class BubbleSort implements SortStrategy {
    @Override
    public void sort(int[] array) {
        for (int i = 0; i  array[j + 1]) {
                    // Swap
                    int temp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = temp;
                }
            }
        }
    }
}

public class QuickSort implements SortStrategy {
    @Override
    public void sort(int[] array) {
        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++;
                // Swap
                int temp = array[i];
                array[i] = array[j];
                array[j] = temp;
            }
        }
        // Swap pivot
        int temp = array[i + 1];
        array[i + 1] = array[high];
        array[high] = temp;
        return i + 1;
    }
}

Here, we implement two sorting algorithms: BubbleSort and QuickSort. Both of these classes implement the SortStrategy interface.

Step 3: Create the Context

The context class will use the strategy:

public class Sorter {
    private SortStrategy sortStrategy;

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

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

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

The Sorter class allows you to set the sorting strategy dynamically and sort the provided array.

Step 4: Using the Strategy Pattern

Now, let's see how to use the strategy pattern:

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

        Sorter sorter = new Sorter(new BubbleSort());
        System.out.println("Sorting using Bubble Sort:");
        sorter.sort(array);
        System.out.println(Arrays.toString(array));

        array = new int[]{5, 3, 8, 4, 2}; // Resetting the array
        sorter.setSortStrategy(new QuickSort());
        System.out.println("Sorting using Quick Sort:");
        sorter.sort(array);
        System.out.println(Arrays.toString(array));
    }
}

This main class demonstrates using the Sorter with the two different sorting strategies.

Best Practices for Implementing the Strategy Pattern

  • Keep Strategies Independently Testable: Ensure each strategy can be tested separately to validate its behavior.
  • Utilize Interfaces: Define a common interface for strategies to ensure consistent implementation.
  • Follow Single Responsibility Principle: Each strategy should encapsulate a specific algorithm and be responsible for that logic alone.

Conclusion

The Strategy Pattern is an effective design pattern that promotes flexibility and maintainability in your code. By using the Strategy Pattern, you can easily switch algorithms at runtime and enhance your code organization.

Want to learn more about Java Core? Join the Java Core in Practice course now!

To learn more about ITER Academy, visit our website.

Scroll to Top