<No-Bluff-Only-Code>
First Lets Assume That we wont be following any of the design pattern or as a matter of fact the “Strategy Design Pattern”
Let’s Take an Example where we have our goto Vehicle class that “drives” in different ways. some can handle traffic Constraints(cityDriving) , some can handle high speed(raceDriving), and others with special off-road capabilities(off-roadDriving). without strategy pattern the code would look something like this.
public class Vehicle {
private String type;
public Vehicle(String type) {
this.type = type;
}
// "Naive" drive method without Strategy
public void drive() {
if ("CITY".equals(type)) {
System.out.println("Driving with city constraints...");
} else if ("RACE".equals(type)) {
System.out.println("Fast track driving...");
} else if ("OFFROAD".equals(type)) {
System.out.println("Driving off-road with 4x4 support...");
} else {
System.out.println("Default driving mode...");
}
}
}
Problem with the above mentioned code is that it deals with if-else block, adding a new driving strategy will result into modifying our existing drive method that violets the Open-Closed Principle( A class should be open to extension but closed for modification).
The Strategy Pattern :
Instead we can define different concrete strategy classes that will contains the exact implementation of the different driving strategies.
- Define a strategy Interface .
public interface Drive {
void drive();
}
2. Implement Concrete Strategies .
public class CityDrive implements Drive {
@Override
public void drive() {
System.out.println("Driving with city constraints...");
}
}
public class RaceDrive implements Drive {
@Override
public void drive() {
System.out.println("Fast track driving...");
}
}
public class OffroadDrive implements Drive {
@Override
public void drive() {
System.out.println("Driving off-road with 4x4 support...");
}
}
3. Inject and Assign Strategy in the vehicle class.
public class Vehicle {
private Drive driveCapability;
public Vehicle(Drive driveCapability) {
this.driveCapability = driveCapability;
}
public void setDriveCapability(Drive driveCapability) {
this.driveCapability = driveCapability;
}
public void drive() {
driveCapability.drive();
}
}
Till this point of time we have create an Interface of Drive , different Concrete Driving Strategy Classes that implements our drive interface and a Vehicle class that does the job of calling the drive method. The Step 3 might be confusing, but just think of this that we hare not hardcoding any strategy in Vehicle Class, the Vehicle class received the Driving Strategy as a parameter when the Class is initialized, or in other words when the constructor code of the class runs. Here the constructor method is Vehicle for Vehicle Class, setDriveCapability has a different use case further which we will see. based on the Concrete class that we pass as a paramater to Vehicle driveCapability we call the drive method of that Concrete Strategy Class. Now Moving to the main method.
public class Main {
public static void main(String[] args) {
// Car with city driving
Vehicle cityCar = new Vehicle(new CityDrive());
cityCar.drive();
// Switch behavior at runtime
cityCar.setDriveCapability(new RaceDrive());
cityCar.drive();
// Offroader
Vehicle offRoadVehicle = new Vehicle(new OffroadDrive());
offRoadVehicle.drive();
}
}
If you observe the main method, cityCar is an Object of Vehicle class that is following cityDrive Strategy Concrete Class, the output after this line of code will be -> “Driving with city constraints…” . Next we are calling setDriveCapability method for cityCar object from Vehicle class and then passing a new Concrete Strategy Class as a parameter, the output after this line of code will be -> “Fast track driving…” . Next again we are declaring another object of type Vehicle named offRoadVehicle and passing in off-road drive Strategy. this will result into -> “Driving off-road with 4x4 support…”
From the Above Implementation its Clear that if we want to implement let’s say another Driving Strategy then we will be creating another Concrete Class for That new Strategy . In this way we are not Modifying the Existing Code , and now the Code looks much easier to manage.