{{announcement.body}}
{{announcement.title}}

Design Patterns Demystified - Strategy Design Pattern

DZone 's Guide to

Design Patterns Demystified - Strategy Design Pattern

In the second article in this series, we take a look at the strategy design pattern that makes it easier to change the methods of implementation class.

· Java Zone ·
Free Resource

In this edition of our series of Design Patterns Demystified, we are going to discuss the strategy design pattern. So let us understand the why, what, how, and where of Strategy Design Pattern.

The Why

Let us first understand why we need this pattern. Imagine you are building an interface for vehicle simulator which has standard behaviors like startEngine(), stopEngine(), drive(), etc. for different types of vehicles, so you created a standard hierarchy of a Vehicle interface and an implementation class for Cars. This worked fine for a while, until you got a contract from an airline company that wants a behavior called fly() instead of drive() (because you don't "drive" planes, generally).

You added a new interface method called fly() and implemented a new class for airplanes which extends from the Vehicle interface (which forced the Car class to have an empty implementation of fly()). Just when things were settling down, you got another contract from a cruise ship company that wants a new behavior called sail() instead of drive() or fly()

You see the point now? This is going to be a never-ending cycle of adding new classes and adding empty method implementations in each derived class. In the future, you might have drones, and rockets and what not. So, you need a better application design. Here, strategy design pattern comes in handy.

The What

Strategy design pattern is a behavioral design pattern that lets you define common behaviors of the application as interfaces themselves. If you have a new implementation demanding a new behavior then you can simply create a new implementation class for that behavior interface and your application interface remains unchanged. This lets you keep your code open for any future changes without altering anything for existing behavior. Another big feature of this pattern is that you don't have to implement empty methods for drive and sail in Airplanes class, and the same is true for Cruise Ship class and Vehicle classes.

The How

Let us use the same example mentioned above to see how we can implement a strategy for the tackling different behaviors of different types of Vehicles like Cars, AirPlanes, and CruiseShips.

So let us create the VehicleSimulator class. It has an Interface named OperatingStrategy which will dynamically accept different operating behaviors like fly(), sail(), etc. This is how the class looks like:

Java
 




xxxxxxxxxx
1
15


 
1
public abstract class VehicleSimulator {
2
    private OperatingStrategy operatingStrategy;
3
    public VehicleSimulator(OperatingStrategy operatingStrategy) {
4
        this.operatingStrategy = operatingStrategy;
5
    }
6
 
          
7
    public void startEngine(){
8
        System.out.println("Engine started");
9
    }
10
    public void stopEngine(){
11
        System.out.println("Engine stopped");
12
    }
13
    //getters and setters
14
}
15
 
          



The interface OperatingStrategy is pretty straightforward. It looks like:

Java
 




xxxxxxxxxx
1


1
public interface OperatingStrategy {
2
    void operate();
3
}


Now every time you have a new vehicle type, you need to add a new implementation of OperatingStrategy and your vehicle simulator and its consumers remain completely unaffected. Below is a sample implementation for Airplanes:

Java
 




xxxxxxxxxx
1


 
1
public class AirPlaneOperatingStrategyImpl implements OperatingStrategy {
2
    @Override
3
    public void operate() {
4
        System.out.println("Airplane is flying now");
5
    }
6
}


Similarly, we will have implementations for Car and CruiseShip as well.

Along with the strategy implementation, you will also create a simulator implementation for Car, AirPlane, and CruiseShip. Below is a sample of the CarSimulator implementation:

Java
 




xxxxxxxxxx
1


1
public class CarSimulator extends VehicleSimulator {
2
    public CarSimulator(OperatingStrategy operatingStrategy){
3
        super(operatingStrategy);
4
    }
5
}


And, now are all set to run this example. Below is the sample code for the Driver program:

Java
 




xxxxxxxxxx
1
11


1
public static void main(String[] args) {
2
        VehicleSimulator[] simulators = {
3
                new CarSimulator(new CarOperatingStrategyImpl()),
4
                new AirPlaneSimulatorImpl(new AirPlaneOperatingStrategyImpl()),
5
                new CruiseShipSimulatorImpl(new CruiseShipOperatingStrategyImpl())
6
        };
7
 
          
8
        for (VehicleSimulator simulator : simulators){
9
            simulator.getOperatingStrategy().operate();
10
        }
11
    }


Running the above Driver program, you will see the following output:

 Land vehicle is steered now 

Airplane is flying now 

Cruise ship is sailing now 

Now, if you observe the Driver program carefully, you will notice that the simulators are completely decoupled from the inner details of the method operate(). All they need to know is the correct strategy object for their simulator type and that's it.

The Where

Strategy pattern should be used whenever you are working in a project with changing requirements (which would always be the case). As we can see, it is extremely critical to the success of any evolving product, so, this design pattern should come naturally to the developers whenever they are designing the architecture of the application. To make the lives of developers easier in the long run, this pattern should be introduced as early in the software development cycle, as possible.

The complete code for this example is available on this link.

Happy learning!!

Topics:
design pattern ,design patterns ,design patterns in java ,design patterns uncovered ,strategy design pattern ,tutorial

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}