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
- Basic programming knowledge
- Knowledge of basic <a href="https://www.devmaking.com/learn/design-patterns/design-pattern-modeling/" target="_blank" style="color:inherit;">Modeling Notation</a>
- Familiarity with <a href="https://www.devmaking.com/learn/algorithms/" target="_blank" style="color:inherit;">Sorting Algorithms</a> is a plus!
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 < arr.length; i++ ) {
int tmp = arr[i];
int j = i - 1;
while ( j >= 0 && arr[j] > 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 < arr.length - 1; i++ ){
int min = i;
for ( int j = i + 1; j < arr.length; j++ ){
if ( arr[j] < 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 < arr.length; i++ ) {
int tmp = arr[i];
int j = i - 1;
while ( j >= 0 && arr[j] > 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 < arr.length - 1; i++ ){
int min = i;
for ( int j = i + 1; j < arr.length; j++ ){
if ( arr[j] < 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
Recommended Resources
- <a target="_blank" href="https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=devmaking-20&linkId=bc32087110669f75d93b216df79816f0" style="color:#fff;border-radius:3px;background-color:#888;padding:1px 5px">Head First Design Patterns: A Brain-Friendly Guide</a> : An excellent primer for learning design patterns in a pragmatic way.
- <a target="_blank" href="https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=devmaking-20&linkId=ae25d94c4ea49870eb3115e0e4d2de90" style="color:#fff;border-radius:3px;background-color:#888;padding:1px 5px">Design Patterns: Elements of Reusable Object-Oriented Software</a> : The reference guide made famous by Gang of Four, still widely used today.
- <a href="https://www.draw.io" target="_blank" style="color:#fff;border-radius:3px;background-color:#888;padding:1px 5px">Draw.io</a>: A free, open-source tool for designing diagrams with built-in support for UML diagrams to make your own!