Strategy Design Pattern

Today I’ll be going through the Strategy Design Pattern. However, before we go in-depth, let's start off with a scenario that may seem all too common to some of us (especially when we become lazy with our code).

What if we wanted to create an application to perform some sort of calculation? Well we could create a method to handle the calculations! But these calculations could vary depending on the choice of the user or the input of the function. How should we handle it? Should we do something like this?

// Assuming only 1 of the inputs are true
calculate(isAddition, isMultiplication, isSubtraction, isDivision) {
if (isAddition) {
// performs addition
} else if (isMultiplication) {
// performs multiplication
} else if (isSubstraction) {
// performs subtraction
} else if (isDivision) {
// performs division

...
}

Well, you get the idea…

After one too many conditions or calculation methods, you may find that your code may end up rather complicated and difficult to test. In addition, even if you abstract the methods of addition, multiplication and all other calculation algorithms out, you end up with a function that is difficult to test and debug! Furthermore, this actually violates the Open-Closed Principle as part of the SOLID principles which dictates that an entity (function, module, class, etc) should be open to extension but closed to modification — but that's not what we are going to talk about today. Today, we will talk about a design pattern that can help resolve such as issue.

Sooo how do we handle this?

We can make use of a design pattern called the Strategy Design Pattern. Well, what is it?

Introduction: Strategy Design Pattern

The Strategy Design Pattern is a behavioural design pattern. A behavioural design pattern is a pattern that defines manners of communication between classes and objects.

Why is it called Strategy? Well its because the pattern enables us to select the right strategy (or behavior) for solving a particular problem.

The basic idea behind the Strategy Design Pattern is that we abstract out the algorithms used inside our method into other classes. All these classes or rather let's call them Strategies (Addition, Multiplication, etc) will implement an interface which will dictate the methods that they use!

Image of Strategy Pattern from Wikipedia

As you can see from the image above, now we have ‘Concrete Strategies’ that implements this Strategy interface. With reference to our example, the ‘Concrete Strategies’ would be Multiplication, Addition, Subtraction and Division! An interface is basically a contract — when a class implements the interface, a class must use all the methods dictated by that interface. In our example, all the ‘Concrete Strategies’ will have their own implementations of ‘calculate’ which is precisely what we want.

Let us make things clearer with some code:

public interface Strategy {
public int calculate(int input1, int input2);
}
public class Multiplication implements Strategy {
@Override
public int calculate(int input1, int input2) {
return input1 * input2;
}
}
public class Addition implements Strategy {
@Override
public int calculate(int input1, int input2) {
return input1 + input2;
}
}
public class Subtraction implements Strategy {
@Override
public int calculate(int input1, int input2) {
return input1 - input2;
}
}
public class Division implements Strategy {
@Override
public int calculate(int input1, int input2) {
return input1 / input2;
}
}

As seen above — observe how each Strategy has the exact same functions — just that they handle the inputs differently and produce different outputs.

Okay! So now we have successfully abstracted away from our ‘Strategies’ from the original method. But how do we use these Strategies?

Context

What we will have is another class, called the ‘Context’. Why is it called the ‘Context’? This is because it provides the context for the strategies to operate. What happens is that the Context will hold a Strategy object. Also, for anyone who is confused the black arrow shown in the above diagram is a ‘composition’ — in other words, should the Context object be deleted, the Strategy object that ‘resides’ inside will also be deleted.

Thus let us show the example with code! Hopefully, it will be clearer

public class Context {    

private Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
public int applyStrategy(int input1, int input2) {
return this.strategy.calculate(input1, input2);
}
}

As shown above, the ‘strategy’ object in the Context class object could be any class that extends the ‘Strategy’ interface!

Magic Time!

So now for the magic to happen…

public static void main(String[] args) {

// Sets the context to hold Addition Strategy
Context context = new Context(new Addition());
// Prints out 15
System.out.print(context.calculate(10, 5));
// Resets the context to hold Multiplication Strategy
context = new Context(new Multiplication());
// Prints out 50
System.out.print(context.calculate(10, 5));
}

Now notice that something magical happens here — what if we want to do Addition then Division? We can simply change the Context!

So now back to our original code. Now we don't need to throw everything inside our ‘calculate’ method. This is because we can make use of Context.

public class Solution {    Context context;    setToAddition() {
context = new Context(new Addition());
}
setMultiplication() {
context = new Context(new Multiplication());
}
setToSubtraction() {
context = new Context(new Subtraction());
}
setToDivision() {
context = new Context(new Division());
}
calculate(input1, input2) {
this.context.calculate(input1, input2);
}
}

See how much cleaner the code is now? In addition we also fulfill the Open Closed Principle mentioned at the start of the article. In this case, we leave the main class open to extension but closed to modification — as all the logic is handled by the different Strategy classes. This also makes our code easier to test!

Also note how the Strategy Design Pattern makes use of polymorphism from the implementation of interfaces to allow us to switch between using difference algorithms / Strategies. Overall, I hope you can see how much cleaner and simpler the code is. It also enhances quality attributes like Readability and Extensibility!

Final Notes: State vs Strategy Pattern

Some of you may also have heard of the State pattern. The Strategy and State pattern may seem quite similar as they both perform some sort of switching of context and state respectively in runtime (with the use of polymorphism). However the difference between them is that the State pattern works to perform state transition while the Strategy pattern works to change the behaviour of a class by changing the context which changes the algorithm.

Anyway, in conclusion, I hope you have learnt a little more about how useful the Strategy Design Pattern is as well as when to use it! Leave any thoughts you have in the comments!

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store