To understand the strategy design pattern we need to first understand what problem it solves and at which situation we can apply this design pattern. A learning design pattern is not easy if you don't understand the design principles
So we will go through the experience of developers and Managers how they think and come up with the design by facing the various hurdle and challenges to solve the design problem.
Here is the imaginary company who make the new app called "SimAduck" the game show the large variety of ducks which can be swimming and making quacking sound. Tom is the manager and Jerry is a very passionate enthusiastic developer.
Jerry comes up with the initial design with Object-Oriented design technique and creates the superclass duck from which all other duck inherits
a few days later Tom comes up with a new requirement that ducks can fly Jerry said don't worry and how hard it can be? he added a new method fly() in the duck class which all ducks inherit, he start thinking that he is an object-oriented genius :)
But something went wrong very horribly !!!!!
Tom called Jerry " I am giving the demo to the stakeholder and rubber ducks are flying everywhere it that your idea of a joke, You might spend some time on noukri.com.
Ok, What when wrong?
Jerry failed to notice that not all the subclasses of duck should fly. when jerry added new behavior to superclass he was also adding the behavior that was not appropriate to some of the ducks subclass for example rubber duck
Localized update to the code caused non-localized side effect (flying the rubber duck)
Now Jerry think about the Inheritance "I could away just override the fly () method in the rubber duck the way I am doing with the quack method, Hmmm .. but then what happens when we add wooden decoy ducks to the program they aren't supposed to fly or quack
What about an Inheritance?
Jerry realized that Inheritance will not solve the problem as he realize that the app will get updated very frequently and new changes integrating will be a nightmare as inheritance is very rigid for incoming changes.
Jerry comes with a new idea. I could take the fly() out of the duck supper class and make a Flyable interface with the fly method so those ducks want to fly they will implement the Flyable interface and I might take out the Quackable interface too since not all duck can quack.
What are you thinking about this design solution? Think a while before reading further
Tome Replied: That is the dumbest idea you come up with. Are you saying "Duplicate code" ? if you are thinking that have to. override a few methods. then think if we need little change in the fly method then this change needs to be done in all 40 to 50 subclasses .which is a nightmare for you to track all subclasses and their behavior.
We know that not all of the subclasses should have flying or quacking behavior, so inheritance isn't the right answer but having the subclasses implement Flyable and or Quackable solves the part of the problem. but it completely destroys the code reusability behavior, so it just creates the different maintenance nightmare problem
And of course, there might be more than one kind of flying behavior even among the ducks that do fly.
So you all may be thinking. Some Thor will come with his Hammer design pattern solution and solve our problem but the good thing is we need to solve this problem by applying the Object-oriented Design principle
Zeroing in on the Problem.
Let's break the problem and failed solution
Inheritance wasn't worked out well. Duck behavior is changing across the subclasses The flyable and Qauckable interface solves the part of the problem but is breaks the reusability.
That means whenever need to modify the behavior then need to track all subclasses behavior.
Luckily there is Design Principle Just to apply this kind of situation.
# Design principle .
Identify the aspects of your application that vary and separate them from what stays the same.
we identified the part which is changing and remains steady. From now on, duck behavior will be separately lived in a separate class that implements the particular behavior that is the way the duck classes won't need to know any of the implementation details from their own behavior
with our new design, duck subclasses will use a behavior represented by the interface Flybehavior and QuackBehaior, so that actual implementation of the behavior won't be locked up into the Duck subclasses.
So fly behavior is an interface that all flying classes implements. all-new flying classes just need to implement the fly method.
Similarly, same thing with the QuackBehavior just include the quack behavior which will be implemented by the subclasses which are only in existence of quack behavior
With this design, other types of objects can reuse our fly and quack behaviors because these behavior are not hidden in the Duck supper class. and we can add new behavior without modifying any of the existing duck classes that use flying behavior. so we get the benefit of reuse without all baggage that comes with the inheritance.
A key aspect of this design duck can delegate the responsibility of flying and quacking behavior instead of the method defined in Duck class and its subclasses.
Now let's integrate the ducks with their behaviors
First, we need to add the two instance variable in the duck class called fly behavior and quick behavior that are declared as the interface types. and remove the fly and quack method from the Duck and its subclass and add the perform and performQuack()
Ok, now it's time to set the flying and quacking behavior object to set.
There are 2 ways
- will accept the flying and quacking behavior from the constructor for initial beharior.
- we can expose the setter to set the behavior dynamically when we need to change behavior as needed so we will do both things as we accept default behavior from the constructor and we allow user to set the behavior on runtime
public interface Iflyable
{
void fly();
}
public class FlyingBehevior1 : Iflyable
{
public void fly()
{
System.Console.WriteLine("flying behavior 1");
}
}
public class FlyingBehevior2 : Iflyable
{
public void fly()
{
System.Console.WriteLine("flying behavior 2");
}
}
interface IQuackable
{
void quack();
}
public class QuackBehavior1 : IQuackable
{
public void quack()
{
System.Console.WriteLine("quack behavior 1 ");
}
}
public class QuackBehavior2 : IQuackable
{
public void quack()
{
System.Console.WriteLine("quack behavior 2 ");
}
}
pubic class Duck
{
public Iflyable iflyable { get; set; }
public IQuackable quackable { get; set; }
public Duck(Iflyable flayble, IQuackable quackable)
{
this.iflyable = flayble;
this.quackable = quackable;
}
void dislay()
{
System.Console.WriteLine("dispay duck");
}
}
public class consumer
{
public static void Main(string[] args)
{
Iflyable flyable = new FlyingBehevior1();
IQuackable quackable = new QuackBehavior1();
//Passing the behavior through constructor.
Duck duck1 = new Duck(flyable, quackable);
//changing the behavior on runtime
duck1.iflyable = new FlyingBehevior2();
}
}
In this way Strategy pattern born and it open enormous ways for our imaginations
I hope you learn from this article . thanks for reading.