Back to: Java Design Patterns
Strategy Design Pattern in Java with Examples
In this article, I am going to discuss the Strategy Design Pattern in Java with Examples. Please read our previous article where we discussed the State Design Pattern in Java with Examples. The Strategy Design Pattern falls under the category of Behavioral Design Pattern. In this article, we will explore the Strategy Design Pattern in Java, its advantages, disadvantages, and its effective utilization in software development.
What is Strategy Design Pattern?
According to the Gang of Four Definitions, define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
In the world of software development, creating flexible and maintainable code is crucial for long-term success. One design pattern that aids in achieving these goals is the Strategy Pattern. The Strategy Pattern is a behavioral design pattern that allows the selection of a specific algorithm or behavior at runtime, enabling dynamic and interchangeable implementations.
The Strategy Pattern revolves around the concept of encapsulating interchangeable behaviors into separate classes, known as strategies. These strategies implement a common interface, enabling the client to utilize different algorithms interchangeably based on specific requirements or conditions. By separating the algorithm’s implementation from the client, the Strategy Pattern promotes loose coupling and enhances code maintainability.
The Strategy Design Pattern is used when we have multiple algorithms (solutions) for a specific task and the client decides on the actual implementation to be used at runtime. To understand better, please have a look at the following image. As shown in the below diagram, we have one task and to solve the task we have three solutions (i.e. Solution 1, Solution 2, and Solution 3). That means using the above three solutions we can achieve the task. As per the Strategy Design Pattern which solution should be used will be decided by the client only at runtime. So, the client will decide whether to use Solution 1 to achieve the task or Solution 2 to achieve the task, or Solution 3 to achieve the task only at run time.
Real-Time Example to Understand Strategy Design Pattern:
Please have a look at the following image. As you can see, in my D drive I have a folder called DotNetDesignPattern and within that folder, multiple text files are there. My business requirement is, I have to compress this DotNetDesignPattern folder and send the compressed file to the client.
For this requirement, I have two solutions. The first solution is to compress the folder into RAR format and send it to the client and the second solution is to compress the folder into ZIP format and sends it to the client. So, for the above requirement, I have two solutions which are shown in the below image.
As per the Strategy Design Pattern, for a Particular Problem (Task), there are multiple solutions and which solution to be used will be decided by the client only at runtime, not at compilation time. So, in our example, the client will decide at runtime in which format he wants the file.
Implementing Strategy Design Pattern in Java:
By using the Strategy Design Pattern, a program can encapsulate different types of operations into separate strategy classes. Each strategy class represents a specific calculation, such as addition, subtraction, or multiplication. These strategy classes implement a common interface, defining methods like “calc()”. At runtime, the application can dynamically select the appropriate operation based on user input.
The Strategy Design Pattern enables the system to switch between different operations seamlessly. It promotes code reusability by encapsulating each sorting algorithm within its own strategy class, allowing them to be used in various contexts and applications without code duplication. It also simplifies the addition of new operations in the future by introducing new strategy classes that adhere to the common interface. The UML Diagram of this example is given below using Strategy Design Pattern.
Step 1: Create a new directory to store all the class files of this project.
Step 2: Open VS Code and create a new project, called strategy.
Step 3: In the project, create a new file called Maths.java. Add the following code to the file:
public interface Maths { public float calc(float num1, float num2); }
This is the interface from which other concrete classes will extend.
Step 4: In the project, create three new files called Addition.java, Subtraction.java, and Multiplication.java. All three of these files extend from the Maths interface. Add the following code to the files:
Addition.java
public class Addition implements Maths { @Override public float calc(float num1, float num2) {return num1 + num2;} }
Subtraction.java
public class Subtraction implements Maths { @Override public float calc(float num1, float num2) {return num1 - num2;} }
Multiplication.java
public class Multiplication implements Maths { @Override public float calc(float num1, float num2) {return num1 * num2;} }
Step 5: In the project, create a new file called Context.java. Add the following code to the file:
public class Context { private Maths m; public Context(Maths m) {this.m = m;} public float calc (float num1, float num2) {return m.calc(num1, num2);} }
Step 6: In the project, create a new file called StrategyPatternDemo.java. This class will contain the main() function. Add the following code to StrategyPatternDemo.java:
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; public class StrategyPatternDemo { public static void main(String[] args) throws NumberFormatException, IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); float num1, num2; System.out.print("Enter the first number: "); num1 = Float.parseFloat(br.readLine()); System.out.print("Enter the second number: "); num2 = Float.parseFloat(br.readLine()); System.out.println(); System.out.println("Addition: " + new Context(new Addition()).calc(num1, num2)); System.out.println("Subtraction: " + new Context(new Subtraction()).calc(num1, num2)); System.out.println("Multiplication: " + new Context(new Multiplication()).calc(num1, num2)); } }
This main() function takes two numbers and performs three operations on them. It then prints out those numbers.
Step 7: Compile and execute the application. Ensure compilation is successful. Verify that the program works as expected.
Congratulations! You now know how to implement strategy patterns!
UML Diagram of Strategy Design Pattern:
Now, let us see the Strategy Design Pattern UML Diagram Components with our Example so that you can easily understand the UML Diagram.
The classes can be described as follows:
- Object: This is the interface that will define the basic methods that will be implemented by the concrete object class.
- ConcreteObject: This class implements the Object interface. It implements the functions defined in the interface.
- Handler: This object is another object that will be used by the main() function. The responsibility of this class is to handle the objects of type Object.
- DriverClass: This class contains the main() function and is responsible for the simulation of the program.
Advantages of Strategy Design Pattern in Java
The advantages of using the Strategy Design Pattern in Java are as follows:
- Flexibility and Extensibility: One of the significant advantages of the Strategy Pattern is its flexibility. It allows developers to introduce new strategies without modifying existing code. By encapsulating each behavior within a separate strategy class, it becomes straightforward to add or replace strategies as needed, making the system more adaptable to changing requirements.
- Code Reusability: The Strategy Pattern promotes code reuse by encapsulating each behavior within its own strategy class. Multiple clients can use these strategies across different parts of the system without duplicating code. This reduces redundancy, improves code readability, and facilitates easier maintenance.
- Simplified Testing: Due to the separation of concerns, testing becomes more manageable with the Strategy Pattern. Each strategy can be tested independently, ensuring the correctness of specific algorithms without affecting other parts of the system. This modularity also facilitates the creation of unit tests, making it easier to identify and isolate issues.
- Runtime Switching: The Strategy Pattern provides the ability to switch between different strategies at runtime. This dynamic behavior enables applications to adapt to varying conditions or user preferences without restarting or interrupting the system. It allows for real-time adjustments, such as changing sorting algorithms, network protocols, or data compression techniques, to optimize performance or meet specific requirements.
- Better Maintenance and Evolvability: Separating algorithms into individual strategies results in code that is easier to understand and maintain. It becomes simpler to locate, modify, or enhance specific behaviors without affecting other parts of the system. This modular approach also enables easy integration of new strategies or modifications, minimizing the risk of introducing bugs in existing code.
Disadvantages of Strategy Design Pattern in Java
The disadvantages of using the Strategy Design Pattern in Java are as follows:
- Increased Complexity: Implementing the Strategy Pattern introduces additional complexity to the codebase. The introduction of strategy classes, along with the management of their instantiation and selection, can lead to a more intricate system structure. Developers need to strike a balance between the benefits of flexibility and the increased complexity caused by the pattern’s implementation.
- Overhead and Performance Impact: The Strategy Pattern adds a layer of abstraction, which may result in some overhead and potential performance impact. Switching between different strategies incurs runtime costs due to additional method calls and indirection. While the impact might be negligible in many cases, it can become significant in performance-critical applications.
- Increased Number of Classes: Utilizing the Strategy Pattern can lead to a proliferation of classes, particularly if the system requires a wide range of interchangeable behaviors. Each behavior requires its own strategy class, and an excessive number of strategies can lead to a bloated class hierarchy, making the codebase more challenging to navigate.
- Dependency Injection Complexity: If the Strategy Pattern is used in conjunction with dependency injection frameworks, additional complexity may arise. The injection of the appropriate strategy instances can be challenging, particularly when the selection of strategies is based on runtime conditions or external factors. Careful consideration is necessary to ensure proper configuration and maintain clarity.
In the next article, I am going to discuss Template Method Design Pattern in Java with Examples. Here, in this article, I try to explain Strategy Design Pattern in Java with Examples. I hope you understood the need for and use of the Strategy Design Pattern in Java.