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.
Join the DZone community and get the full member experience.Join For Free
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.
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.
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.
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:
The interface OperatingStrategy is pretty straightforward. It looks like:
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:
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:
And, now are all set to run this example. Below is the sample code for the Driver program:
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.
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.
Opinions expressed by DZone contributors are their own.