Learn the Strategy Design Pattern
This is the 18th post in a series on design patterns.
Strategy is a behavioral design pattern that lets you define a family of algorithms, put each of them into a separate class, and make their objects interchangeable.
Imagine you want to create an online application to sort numbers. For a small number of numbers, the app relied on the Bubble Sort, whose time complexity was O(n²).
There were many requests for introducing another sorting algorithm, the Quick Sort or the Merge Sort O(nLogn), which is far superior to Bubble Sort.
Every time you added a new sorting algorithm, the main class became too difficult to maintain. Changes to any of the algorithms, whether it was a simple bug or optimizing the algorithm logic, affected the whole class, increasing the chance of an error occurring in previously working code.
Strategy Design Pattern
A strategy design pattern suggests that you take a class that performs something specific in a variety of ways and extract all of these algorithms into their own classes.
A reference to one of the strategies must be stored in the original class, called context. Instead of executing the work on its own, the context delegated the task to a linked strategy object.
The context does not determine which algorithm is appropriate for the job. Rather, the client passes the desired strategy to the context. In fact, the context does not have any idea about strategies. This generic interface works with all strategies and exposes only a single method for triggering an algorithm encapsulated within a selected strategy.
In this way, the context becomes independent of concrete strategies, so you can add new algorithms or modify existing ones without changing the code of the context or other strategies.
UML Class Diagram
Not familiar with UML Class Diagram? I have written a detailed post on the UML Class diagram.
- Identify an algorithm in the context class that tends to change frequently. At runtime, it can also be a massive conditional that selects and executes a variant of the same algorithm.
- Define the strategy interface common to all variants of the algorithm.
- Extract all algorithms into individual classes one by one. Each class must implement the strategy interface.
- Add a field in the context class for storing a reference to a Strategy object. Provide a setter to replace the value of that field. The context should only interact with the strategy object through the strategy interface. The context may define an interface that allows the strategy to access its data.
- The context must be associated with a strategy that matches the way clients expect it to perform its primary function.
Source Code Implementation
The NumberSorter Context class maintains a reference to one of the concrete strategies and communicates with it only through the strategy interface.
All concrete strategies share the SortingStrategy interface. It defines a method that the context uses to execute the strategy.
The BubbleSort and QuickSort concrete Strategy implements different variations of an algorithm that the context uses.
Each time it needs to run the algorithm, the context calls the execution method on the linked strategy object. The context doesn’t know which strategy it uses or how the algorithm works.
A StrategyClient creates a specific strategy object and passes it to the context. During runtime, clients can replace the strategy associated with the context using the context’s setter.
Original Array: [1, 2, 4, 6, 17, 70, 34, 23, 60, 20, 56, 89, 7]
Sorted Array: [1, 2, 4, 6, 7, 17, 20, 23, 34, 56, 60, 70, 89]
Original Array: [10, 20, 40, 16, 27, 30, 32, 3, 0, 2, 86, 9, 7]
Sorted Array: [0, 2, 3, 7, 9, 10, 16, 20, 27, 30, 32, 40, 86]
When To Apply Strategy Design Pattern
- If you want to use different variants of an algorithm within an object and be able to switch from one algorithm to another during runtime, you should use the Strategy pattern.
- When you have a lot of similar classes that only differ in the way they behave, use the Strategy pattern.
- It allows you to separate the business logic of a class from the implementation details of algorithms that may not be as crucial to the logic.
- When your class has a conditional operator that switches between several variants of the same algorithm, use the pattern.
Pros of Strategy Design Pattern
- At runtime, you can swap algorithms used within an object.
- Implementation details of an algorithm can be separated from the code that uses it.
- Inheritance can be replaced with composition.
- You can introduce new strategies without having to change the context.