Saturday, 21 November 2015

In Detail: Strategy Design Pattern In Java

In this post i am going to explain about Strategy Design pattern. First of all, 
What is Strategy Design Pattern?

Let's see the definition first.

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.

Very hard to understand, right? This is a high level explanation of Strategy design pattern. Don't explain this definition to beginners.

Let's see what is Strategy design pattern in detail, with an example.

first, it is a behavioral design pattern.

To understand this pattern, you should know the below terminology & concepts of Object oriented programming language.

1. Inheritance,
2. Composition,
3. IS-A , HAS-A relationship,
4. Interface driven programming,
5. Polymorphism.

Let's start with an example problem.

Consider, we have a base class Animal and 2 sub classes Parrot, Cat (Inheritance).
package com.speakingcs.designpatterns.strategy;

public class Animal {

    private String name;

    private String sound;

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this.name = name;

    }

    public String getSound() {

        return sound;

    }

    public void setSound(String sound) {

        this.sound = sound;

    }

}

package com.speakingcs.designpatterns.strategy;

public class Parrot extends Animal {

    public Parrot() {

        setName("Rio");

        setName("Squawk");

    }

}

package com.speakingcs.designpatterns.strategy;

public class Cat extends Animal {

    public Cat() {

        super();

        setName("kitty");

        setSound("Meow");

    }    
}
Here Sound is the behavior of Both Cat and Parrot (Behavior). Now we need to add "fly" behavior to Cat & Parrot. The behavior of cat is "can't fly" and behavior of parrot is "Can fly". How can we solve this problem.

The first solution that comes into mind is adding a method in Animal class(Base Class) and overriding in Cat & Parrot classes (sub classes).
The code looks like below in sub classes.
in Animal class:
 
public void fly() {    

    System.out.println("Can Fly");

}

in Cat class ( override fly() ):

public void fly() {

    System.out.println("Can't Fly");

}

in Parrot class ( get's from parent by default) :

public void fly() {

    System.out.println("Can Fly");

}

 This looks fine solution, but for now we have 2 sub classes only, what if you have 1000 sub classes, in which 500 animals can fly and 500 can't fly. Do you override in each and every sub class. This adds code duplication. For Cat class we don't need a fly() behavior at all, but because of inheritance we are overriding in Cat, other wise we get default behavior from parent.

 And, this is a simple example, but in real world problems, classes will have complex behaviors (thousands lines of code) not like our fly() method. one more problem is, if you want to change the behavior in Cat( add or remove code from fly()) , then you need to change in all subclasses, because you have duplicated in all subclasses. Think what if we need to add some more methods similar to fly() ?

 so we got two problems with the above approach, 1. Code duplication,
 2. Maintenance overhead.

One more approach we can think is to extract fly() method to an interface for eg: Flyable. But in this case we need to define method in each and every class which implements Flyable interface. So we get same problems mentioned above i.e code duplication, maintenance overhead.

Now, let's see how to solve this problem, using Strategy Design Pattern.
We now know, the fly behavior changes between Cat and Parrot. so we extract this method from Animal class to an interface.
interface Flyable {

    public void fly();

}
now create classes and implement Flyable interface. i am going to create 2 classes, one is for "can fly" behavior and another is for "can't fly" behavior.
public class CanFly implements Flyable {

    public void fly() {

        System.out.println("can fly");

    }

}

public class CanNotFly implements Flyable{

    public void fly() {

        System.out.println("Can't fly");

    }
}
Now you've seperated the behaviour from Animal class. Now you need to declare a variable of type Flyable (interface type), and setter method in Animal class.
Flyable aFlyable;

public void setaFlyable(Flyable flyable) {

    aFlyable = flyable;

}
now create one more method in Animal class and delegate behavior to the interface.
public void makeFly() {

    aFlyable.fly();

}
now the sub classes, Cat and Parrot inherit aFlyable from Animal. Using setaFlyable() method, we will set it's Flying ability i.e CanNotFly or CanFly.

in both Parrot, Cat classes, we set their flyable abilities.
package com.speakingcs.designpatterns.strategy;

public class Parrot extends Animal {

    public Parrot() {

        setName("Rio");

        setName("Squawk");

        setaFlyable(new CanFly());

    }

}

package com.speakingcs.designpatterns.strategy;

public class Cat extends Animal {

    public Cat() {

        super();

        setName("kitty");

        setSound("Meow");

        setaFlyable(new CanNotFly());

    }

}
That's it, we have applied Strategy Design pattern to our problem.Now let's understand the definition.
"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."

a. Here, CanFly & Can'tFly are family of algorithms,
b. These are interchangeable, because they are from same super type i.e Flyable. Using polymorphism we can interchange them. For example, if you want to create a Dog class, you can use CanNotFly algorithm, and if you want to create a Duck class, you can use CanFly algorithm. So they vary independently from clients.

When to use Strategy Design Pattern?

 If you've got some aspect of your code that is changing, say with every new requirement, then you know you've got a behavior that needs to be pulled out and separated from all the stuff that doesn't change. Take the parts that vary and encapsulate them, so that later you can alter or extend the parts that vary without affecting those that don't.

Result: fewer unintended consequences from code changes and more flexibility in your systems.

Advantages:

1. Avoid duplicate Code.
2. Keeps class changes from forcing other class changes.
3. can hide complicated / secret code from the user.
4. often reduces long list of conditions.

Negative: Increased Number of Objects /Classes.

0 comments:

Post a Comment

Note: only a member of this blog may post a comment.