- 6k Enrolled Learners
- Live Class
Welcome to the first post of “Design Patterns Exposed” series. In this series we are going to uncover each Design Pattern from scratch.
Simply knowing a programming language and its constructs will not make you a better programmer or developer. It requires knowledge of Design Patterns to create software that will work today and also in future.
Many developers have already come across those design problems that you are facing right now or will face in the future. They have specified a standard way of dealing with that problem. So by using Design Patterns you get the advantage of using proven techniques.
Each Design Pattern is for solving a particular kind of situation; there might be situations where more than one Design Pattern can be used.
Most of the programmers just try to solve the problem they face without bothering about design patterns, redundant code or even tight-coupling. But good programmers start differently. They think about today’s requirements, future requirements, maintenance of code and re-usability of code.
Good programmers are not in a hurry to start coding once they get the requirements. They sit and think about the problem; about whether their design will work. If yes, whether it will work after 6 months, when requirements will change.
Good programmers take their pen and paper and start designing their classes and relationship between classes. They try to get loose coupling and high cohesion in their design, while doing all these they have Object-Oriented Principles in their mind. They don’t go inside low-level code immediately. To design flexible and reusable software, you should follow this approach; otherwise you will always find yourself modifying code that you had written earlier.
There is only one thing that is constant in software industry and that is Change. The requirements will certainly keep changing. So how do we design the software that your code can easily adapt to future requirements? For that you have to start early, and design it in such a way that future requirements doesn’t break your previous code.
Well, it can be done by following Design Principles and Design Patterns based on those principles.
Now, let’s dive into coding and get started on the journey to become a better programmer. In this post, we are going to uncover one of the most important pattern – Strategy Pattern.
When I say the most important it reflects on the common problem that is solved by Strategy Pattern.
Here is the definition straight from the ‘Gang of Four’ book: “The Strategy Pattern is used to create an interchangeable family of algorithms from which the required process is chosen at run-time”.
In case you are not able to understand, don’t worry, we are going to explain it in a simpler way for you to understand.
Let’s first understand the problem and then we will see how Strategy Pattern can solve that.
In the above UML diagram, we have Animal abstract class and two concrete classes, Dog and Bird, extending from Animal super class.
So let’s define an Animal abstract class and two concrete classes, Dog and Bird.
What you think about the above design? There is one big mistake in our design.
All the animals cannot fly, like in the above case a dog cannot fly. But still it has ‘fly’ behavior.
We made a mistake by writing the abstract fly () method inside Animal class. This design will force each sub-class Dog, Bird, Penguin, Crocodile, Goose etc. to implement fly () method.
We should have understood that flying is an ability that not all animals will have. By providing fly () method in Animal abstract class we have set the flying ability in all sub-classes which is not correct for all sub-classes of animals.
You might think what’s the problem in implementing the fly method in the sub-classes. Although you can implement the fly () method in the non-flying Animal sub-classes to just print “I can’t fly”. But the problem is, you are still giving the fly behavior to non-flying animals. This is not correct.
How does it feel to call dog.fly () or crocodile.fly () .
So, now we have understood that our design is not correct and we should remove the fly () method from the Animal sub-class.
What is the other way of designing our classes in a way that our design do not enforce all Animal sub-classes to have fly behavior.
One solution that immediately comes to mind is that we can make a flying interface having fly method and only animals that can fly will implement that flying interface. This way we will not enforce all Animal sub-classes to define a fly behavior. So let’s code this design approach.
Now, our Animal class will look like the below code after removing the fly method from the Animal class.
Now let’s define the Flying interface
Now, Dog class will be changed as the code below and it does not need to have fly behavior.
We have solved our previous problem, but we got into a new trouble and that is “Code Duplication”.
Say, we are going to have 100 different flying Animal sub-classes. We have to duplicate the code for fly behavior since flying interface cannot provide any implementation for fly behavior, and later if we want to change the fly () method implementation in any sub-class we will have to open that class and change the code, which is bad. We are lacking something big and, that is, we cannot change the flying behavior of a class at run-time.
But don’t worry, Strategy Pattern is there to get you out of this problem.
So let’s refactor our code to use Strategy Pattern.
Flying interface will remain the same as it is. Now, rather than each flying sub-class implementing the flying interface itself, we are going to define separate concrete classes that will implement different flying behavior. Let’s see how to do that.
So, how it all works,let’s see the TestClass
By using Strategy Pattern we are now able to change the flying behavior of any animal at run-time and that is without enforcing any sub-classes to specify the flying behavior itself.
When you want to be able to change the behavior at run-time dynamically.
In the above Employee class we are setting the pay of the employee depending on his/her designation. If an employee is an “Intern” we are adding 10% bonus in the basic salary to calculate the actual pay.
If an employee is a “Web Developer” we are adding 20% bonus in the basic salary to calculate the actual pay and the similar process follows for other types of employees. Although our algorithm for calculating the actual pay is very simple to make it easier to understand but most of the time, it includes many comparisons and calculations.
So, what’s wrong with employee-class code ?
Well, the code for calculating pay (getPay() ) is static. Suppose I want to change the bonus for “Intern” from 10% to 14%. I will have to open the Employee-class code and change it.
And another problem is I cannot change an employee’s pay algorithm at run-time. So, how to do that? Strategy Pattern is specifically used for handling this kind of problem.
Let’s refactor the code to use Strategy Pattern.
I am going to define several algorithms to calculate pay. Then I will be able to use any of these algorithms to calculate pay at run-time.
Now, let’s see how Employee class is going to change.
Note: I have removed the pay calculation logic from Employee class and created a set PayAlgorithm () method through which I will set the PayAlgorithm that I want to use for pay calculation.
This will give me the flexibility to calculate the pay by specifying any PayAlgorithm dynamically at run-time. Also, note that later if I have to change the pay calculation logic I can create a new PayAlgorithm and use that to calculate the pay. I don’t need to change the previous code, isn’t it great?
So let’s see it working.
I hope you understood the Strategy Pattern very well. The best way to learn something is by practicing.
In case you have any queries relating to Strategy Pattern or any other Pattern, leave your queries below.
Watch out for the next post, where we will uncover one of the most popular Design Pattern, Factory Pattern.
Till then you can download the code; play with it and make sure you cement the Strategy Pattern in your head.
Got a question for us? Mention them in the comments section and we will get back to you.