Design Patterns (Strategy Pattern) Part - II

Introduction
 
As we all have a brief idea of what design patterns and what role it plays in the Software Development Life Cycle. Now we will look into one of the most important pattern known as "Strategy Pattern". Lets start up with an example.

In this Article we will also use the following design principles:

  • Identify the aspects of your application that vary and separate them from what stays the same.
  • Program to an interface, not an implementation
  • Favor Composition over inheritance

Problem (WorldCarSimulator)

DesignPatterns1_new.gif

A simulator which demonstrates the types of worldwide cars and their features. Users can choose any type of cars, then the simulator should give a demonstration of the type of car selected with the features available for that car.
 
Start with the basic design
 
Consider the following basic design:
 
DesignPatterns2_new.gif 

Here Car is the base class. Taxi and RentedCar's are the derived classes.

Carry() is defined in base class and run() is overridden in the derived classes as required.

Suddenly you recognise that, as this is for worldwide cars, there are some cars, which go for race. 
 
DesignPatterns3_new.gif
 
Only change required is to add a race() method in the base class so some cars can go for the race.
 
Update the code with race() method and now the code is ready for demo.
 
User starts the Demo, Some thing went horribly wrong.

"Taxi's started racing"
"RaceCar's started carrying passengers"

Now code needs to be modified.

Modify Taxi Class

  • Race() to do nothing

Modify RaceCar Class

  • Carry() to do nothing

Consider about ToyCar they do not race nor they do not Carry.
 
Create the ToyCar Class

  • Race() to do nothing
  • Carry() to do nothing

DesignPatterns4_new.gif 

As long as new type of car's come, the more modification is required. Let us think about some alternative solution like the following below.
 
DesignPatterns5_new.gif 

In the above design, there is no reusability. As long as you add classes you need to override the methods carry() or race() etc. so this is not a good design.
 
Let us use Design Principle one:

"Identify the aspects of your application that vary and separate them from what stays the same"

DesignPatterns6.gif 

Like this :
 
And one more thing we are using classes where in we do not have a chance for dynamic behaviour. Ie... for example Taxi started taking passengers and after some time it want to go for a race, which is not possible as per the last design.
 
Ok. Lets implement the second design principle:

"Program to an interface, not an implementation"

Let see the design now using the two principles:
 
DesignPatterns7_new.gif
 
We just had designed for the behaviour of Car's like Carry, Race etc..
 
Now we need to design the Types of Cars. It's now time to use the third principle.

"Favor Composition over inheritance"

Here is the final design:
 
DesignPatterns8_new.gif

At last we had implemented the first design pattern
 
"Strategy Pattern"

"Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it."

Now the code walk through:
 
Car Class

Public abstract class Car
{
CarryBehaviour carryBehaviour;
RaceBehaviour raceBehaviour;
public Car()
{
}
public abstract void run();
public void race()
{
raceBehaviour.race();
}
public void carry()
{
carryBehaviour.carry();
}
public void setraceBehaviour(RaceBehaviour rb)
{
raceBehaviour = rb;
}
public void setcarryBehaviour(CarryBehaviour cb)
{
carryBehaviour = cb;
}
}

RaceBehaviour (Interface), RaceCar (Class) & RaceNoWay (Class):
 
public interface RaceBehaviour
{
public void race();
}
public class RaceCar implements RaceBehaviour
{
public void race()
{
system.
out.println("I am racing");
}
}
public class RaceNoWay implements RaceBehaviour
{
public void race()
{
system.
out.println("I can't race");
}
}
 
 
CarBehaviour (Interface), CarryPeople (Class) , NonCarrier (Class) & CarryLoad (Class):
 
public interface CarryBehaviour
{
public void carry();
}
public class CarryPeople implements CarryBehaviour
{
public void carry()
{
system.
out.println("I can carry only people");
}
}
public class NonCarrier implements CarryBehaviour
{
public void carry()
{
system.
out.println("I can't carry");
}
}
public class CarryLoad implements CarryBehaviour
{
public void carry()
{
system.
out.println("I can carry only Load");
}
}

 
Car Class
 
Public class Taxi extends Car
{
public Taxi()
{
carryBehaviour =
new CarryPeople();
raceBehaviour =
new RaceNoWay();
}
public void run()
{
System.
out.println("Running a Taxi");
}
}

Jeep Class
 
Public class Jeep extends Car
{
public Taxi()
{
carryBehaviour =
new CarryLoad();
raceBehaviour =
new RaceNoWay();
}
public void run()
{
System.
out.println("Running a Jeep");
}
}

 
RaceCar Class
 
Public class RaceCar extends Car
{
public Taxi()
{
carryBehaviour =
new NonCarrier();
raceBehaviour =
new RaceCar();
}
public void run()
{
System.
out.println("Going for Race");
}
}
 
 
ToyCar Class
 
Public class ToyCar extends Car
{
public Taxi()
{
carryBehaviour =
new NonCarrier();
raceBehaviour =
new RaceNoWay();
}
public void run()
{
System.
out.println("Play with ToyCar ");
}
}

RentalCar Class
 
Public class RentalCar extends Car
{
public Taxi()
{
carryBehaviour =
new CarryPeople();
raceBehaviour =
new RaceNoWay();
}
public void run()
{
System.
out.println("I am Rented Car");
}
}
 
 
Small Demo
 
Public class WorldCarSimulator
{
public static void main(string[] args)
{
Car Ferrari =
new RaceCar();
Ferrari.carry();
Ferrari.race();
Ferrari.run();
Car RoadRunnerTaxi =
new Taxi();
RoadRunnerTaxi.carry();
RoadRunnerTaxi.race();
//Changing the behaviour dynamically
RoadRunnerTaxi.setraceBehaviour(new RaceBehaviour());
RoadRunnerTaxi.race();
}
}

 
Strategy Pattern Explained
 
DesignPatterns9-new.gif
  
Participants 
 
The classes and/or objects participating in this pattern are:

  • Strategy (SortStrategy)
    • declares an interface common to all supported algorithms. Context uses this interface to call the algorithm defined by a ConcreteStrategy 
  • ConcreteStrategy (QuickSort, ShellSort, MergeSort)
    • implements the algorithm using the Strategy interface
  • Context (SortedList) 
    • is configured with a ConcreteStrategy object
    • maintains a reference to a Strategy object 
    • may define an interface that lets Strategy access its data.
  • Observer (IInvestor)
    • defines an updating interface for objects that should be notified of changes in a subject. 
  • ConcreteObserver (Investor)
    • maintains a reference to a ConcreteSubject object
    • stores state that should stay consistent with the subject's
    • implements the Observer updating interface to keep its state consistent with the subject's