Strategy Pattern

Tanish
5 min readMar 4, 2022

--

Welcome to the design pattern series. Here we will cover all about the design patterns mentioned in the book Head First Design Pattern, if you want to learn the reasoning behind design patterns I highly recommend this book. if you want to learn it quickly or want to revise it, then check out this series. We will pick similar examples as in the book and will attach links to some more examples with UML diagrams. Let's jump into it-

Prerequisite: You must be comfortable with OOPS principles.

Congratulations!!! We won a contract to build a SimUDuck App, that can show a large variety of ducks making quacking sounds. We are assuming the appearances of all ducks are different. So the first design that crosses our thought is

Fig 1

This is a simple design. And according to our requirements, it seems to perform well. We have different appearances so we are overriding the display() method in all subclasses and inheriting ‘quack’ behaviour. So far, so good.

But now, we have got some competitors in the market. Therefore, we need to increase our functionality. Now we also desire to have ‘fly’ behaviour to beat others.

At first, it looks easy just add the fly() method in duck class and all other classes will inherit it. No issue at all. Let's try to run this on screen. Are rubber ducks flying??. But they can’t, right? of course inanimate objects cant fly. How to fix it? Let's see!

To fix this, let's try to override the fly() method. It looks great. Ducks can have different fly behaviour, different appearances, and all can quack. This design looks great.

Let's see how our design will behave when we have too many ducks. If we have 20–30 different categories of duck, then we need to override fly() method in all duck classes.

Here we can see, we have same type of fly behaviour in wild duck and in forest duck and also of sea duck and ocean duck. So basically, Its a replication of code and its not a good practice. And if we wish to slightly change flyBehaviour then we need to update all classes to make a slight change. That will be a maintenance nightmare for us. Here comes our first design principle to rescue us:

Take the parts that vary and encapsulate them, so that later you can alter or extend the parts that vary without affecting those that dont.

Remember solution to the problem with inheritance is not more inheritance.

In our design, fly and QuackBehaviour are varying, so we will encapsulate them. We will create different behavioural classes of fly and quack and they all will implement same respective fly and quack interface so that we can compose them in our duck class. This is our most efficient design as of now later we will see how to use other patterns with strategy.

View in full resolution

As you can see, here we have two interfaces FlyBehavior and QuackBehaviour. Both of them are implemented by different classes corresponding to them. We are composing FlyBehaviour and QuackBehaviour inheritance in Duck class. By this, we can change behaviour at run time.

We have used two more design principles, they are

Program to interface, not an implementation

Program to interface means program to supertype so that we can change behaviour at run time. As in this example we are doing with flyBehaviour and QuackBehaviour. The advantage of this approach is that we can initialize flyBehaviour with any of FlyWithWings or FlyNoWay similar to QuackBehaviour at run time.

Favor composition over inheritance

We could have also inherited flyBehaviour and QuackBehaviour but the disadvantage is that we are hard-coding them. We cant update behaviour at runtime.

Lets look at the formal definition of Strategy Algorithm:

The strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable.Strategy lets the algorithm vary independently from clients that use it.

As we have different variants of algorithm fly, quack and display, could have more like eat,swim, etc. We are encapsulating each algorithm and can interchange their variant at run time like FlyWithWings, FlyNoWay.

TakeAway:

a.Use strategy pattern when we want to use different variants of algorithms and want to interchange between them.

b.Use when we have similar behaviour in classes.

That’s it. We have successfully acquired and implemented our first design pattern. Here are some examples to get a better understanding of strategy pattern.

Example1: We need to build a war game that has some characters. They all can fight and can use different weapons, horses and shields to win the battle. We need to make a design for this system.

Make your own assumptions where ever required and try it.

Solution: Let’s try to find out what can vary — weapons can vary, horses can vary and shields can also vary. So we will encapsulate them and separate them out. Why? So that we can change their behaviour at runtime. Let's look at our design -

View in full resolution

Example2: let's try to build an aeroplane system that shows the functionality of display and flight behaviour of various aeroplanes. Appearances of all aeroplanes are different so we will make this method abstract and fly behaviour can vary so we will encapsulate it. Let's look at our design

Thank you so much for going through it. I hope you got the clarity of the strategy pattern and its implementation and uses. I have attached code and UML diagrams for more clarity. Please find them. And if anyone would like to discuss more, please ping me on LinkedIn and if you are interested to learn more about design patterns, stay tuned for the next post.

SimUDuckApp: https://github.com/excelo0702/StrategyPattern

BattleField: https://github.com/excelo0702/Weapon
UMLdiagrams:https://drive.google.com/drive/folders/1I_YDc3BRROLGakcCyqVwpMhouX-R_Q3D?usp=sharing

--

--

Tanish

Software Developer at Salesforce- Design Pattern Enthusiast, MNNIT Allahabad