Open In App

Strategy Method Design Pattern in Java

Last Updated : 15 Jan, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

Strategy method or Strategy Design Pattern is a behavioral design pattern in Java that defines a family of algorithms, encapsulates each algorithm, and makes them interchangeable. It lets the client algorithm vary independently from the objects that use it.

This pattern is useful when you have a family of algorithms and want to make them interchangeable without modifying the client code.

Strategy-Method-Design-Pattern-in-Java

Key Concept of Strategy Method Design Pattern

In Java, the Strategy pattern is typically implemented using interfaces and classes. Here are the key elements of the Strategy pattern:

Context

  • The context is the class that contains a reference to the strategy interface.
  • It is the class that delegates the algorithm responsibilities to the strategy.

Strategy Interface

  • The strategy interface defines a set of methods that encapsulate the different algorithms.
  • All concrete strategy classes must implement this interface to provide their specific implementations.

Concrete Strategies

  • These are the actual algorithm implementations.
  • Each concrete strategy class implements the strategy interface, providing a specific behavior or algorithm.

Client

  • The client is responsible for selecting a concrete strategy and setting it in the context.
  • It can switch between different strategies dynamically at runtime.

Context-Strategy Interaction

  • The context class contains a reference to a strategy object and delegates the algorithm responsibilities to the strategy.
  • The context class does not implement the algorithm itself but relies on the strategy interface.

Dynamic Strategy Switching

  • One of the key features of the Strategy Method Pattern is the ability to switch between different algorithms dynamically at runtime.
  • The client can change the strategy associated with the context without altering its structure.

Encapsulation of Algorithms

  • Each algorithm is encapsulated within its own strategy class, promoting code reusability and maintainability.
  • Changes to one algorithm (strategy) do not affect the other algorithms, as they are independent and encapsulated.

Flexibility and Extensibility

  • The Strategy Method Pattern allows for a high degree of flexibility in selecting algorithms.
  • It is easy to add new strategies without modifying existing code, making the system extensible.

Promotes Code Reusability

  • Since each algorithm is encapsulated in its own strategy class, they can be reused in different contexts or scenarios.

In summary, the Strategy Method Pattern is a powerful way to define a family of algorithms, encapsulate each one, and make them interchangeable. It provides flexibility, maintainability, and promotes a clean separation of concerns in software design.

Problem Statement of Strategy Method Design Pattern in Java:

Problem Statement:

Suppose we are building an e-commerce application, and we need to implement a payment system that supports multiple payment methods. Each payment method has a different algorithm for processing payments. we want to make the payment processing behavior interchangeable so that we can easily add new payment methods in the future without modifying the existing code.

Explanation of above problem Statement:

In this example, the ‘PaymentStrategy’ interface defines the contract for all payment strategies, and concrete implementations (‘CreditCardPayment’, ‘PayPalPayment’, ‘BitcoinPayment’) encapsulate the payment algorithms. The ‘ShoppingCart’ class acts as the context and can dynamically switch between different payment strategies. This allows for easy extension by adding new payment methods without modifying the existing code.

Overall Code explanation of above Problem Statement:

Java




// PaymentStrategy interface defines the contract for all payment strategies
interface PaymentStrategy {
    void pay(int amount);
}
 
// Concrete implementations of PaymentStrategy for different payment methods
 
class CreditCardPayment implements PaymentStrategy {
    private String creditCardNumber;
 
    public CreditCardPayment(String creditCardNumber) {
        this.creditCardNumber = creditCardNumber;
    }
 
    @Override
    public void pay(int amount) {
        System.out.println("Paid " + amount + " using credit card number: " + creditCardNumber);
    }
}
 
class PayPalPayment implements PaymentStrategy {
    private String email;
 
    public PayPalPayment(String email) {
        this.email = email;
    }
 
    @Override
    public void pay(int amount) {
        System.out.println("Paid " + amount + " using PayPal account: " + email);
    }
}
 
class BitcoinPayment implements PaymentStrategy {
    private String bitcoinAddress;
 
    public BitcoinPayment(String bitcoinAddress) {
        this.bitcoinAddress = bitcoinAddress;
    }
 
    @Override
    public void pay(int amount) {
        System.out.println("Paid " + amount + " using Bitcoin address: " + bitcoinAddress);
    }
}
 
// Context class that uses the PaymentStrategy
class ShoppingCart {
    private PaymentStrategy paymentStrategy;
 
    public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
        this.paymentStrategy = paymentStrategy;
    }
 
    public void checkout(int amount) {
        paymentStrategy.pay(amount);
    }
}
 
// Client code
public class Main {
    public static void main(String[] args) {
        ShoppingCart cart = new ShoppingCart();
 
        // Selecting payment methods dynamically
        cart.setPaymentStrategy(new CreditCardPayment("1234-5678-9012-3456"));
        cart.checkout(100);
 
        cart.setPaymentStrategy(new PayPalPayment("john.doe@example.com"));
        cart.checkout(50);
 
        cart.setPaymentStrategy(new BitcoinPayment("1AbCdEfGhIjK1LmNoPqRsTuVwXyZ"));
        cart.checkout(200);
    }
}


Output

Paid 100 using credit card number: 1234-5678-9012-3456
Paid 50 using PayPal account: john.doe@example.com
Paid 200 using Bitcoin address: 1AbCdEfGhIjK1LmNoPqRsTuVwXyZ





Diagrammatic Representation of Strategy Method Design Pattern in Java

The working of Strategy Method pattern:

Strategy-Method-Design-Pattern-in-Java-(1)

Define the Strategy Interface

Create an interface that declares the methods common to all supported algorithms. This interface is the strategy interface.

Java




public interface PaymentStrategy {
    void pay(int amount);
}


Implement Concrete Strategies

Create one or more concrete classes that implement the strategy interface. Each concrete class represents a specific algorithm.

Java




public class CreditCardPayment implements PaymentStrategy {
    private String cardNumber;
    private String name;
 
    public CreditCardPayment(String cardNumber, String name) {
        this.cardNumber = cardNumber;
        this.name = name;
    }
 
    @Override
    public void pay(int amount) {
        System.out.println(amount + " paid with credit card");
        // Additional credit card payment logic
    }
}
 
public class PayPalPayment implements PaymentStrategy {
    private String email;
 
    public PayPalPayment(String email) {
        this.email = email;
    }
 
    @Override
    public void pay(int amount) {
        System.out.println(amount + " paid with PayPal");
        // Additional PayPal payment logic
    }
}


Create Context Class

Create a context class that contains a reference to the strategy interface. This class delegates the work to the strategy object.

Java




public class ShoppingCart {
    private PaymentStrategy paymentStrategy;
 
    public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
        this.paymentStrategy = paymentStrategy;
    }
 
    public void checkout(int amount) {
        paymentStrategy.pay(amount);
    }
}


Client Code

In the client code, create instances of the strategy classes and set them in the context class. The client code can then use the context class to perform operations using the selected strategy.

Java




public class Main {
    public static void main(String[] args) {
        ShoppingCart cart = new ShoppingCart();
 
        // Use Credit Card for payment
        cart.setPaymentStrategy(new CreditCardPayment("1234-5678-9876-5432", "John Doe"));
        cart.checkout(100);
 
        // Use PayPal for payment
        cart.setPaymentStrategy(new PayPalPayment("john.doe@example.com"));
        cart.checkout(50);
    }
}


In this example, the ‘PaymentStrategy’ interface defines the method pay(), and there are two concrete implementations (‘CreditCardPayment’ and ‘PayPalPayment’). The ‘ShoppingCart’ class is the context that uses a ‘PaymentStrategy’ and delegates the payment to the selected strategy.

This pattern provides flexibility by allowing the client to choose the appropriate strategy at runtime, and it also promotes code reuse and maintainability by encapsulating the algorithm-specific code in separate classes.

Use cases of Strategy Method Design Pattern in Java

Here are some common use cases for the Strategy Method Pattern:

  • File Format Converters: Suppose we have an application that needs to convert data between different file formats (e.g., CSV to JSON, XML to YAML). we can use the Strategy Pattern to define a family of algorithms for each conversion strategy.
  • Algorithm Variations: Sorting Algorithms: If we have a sorting algorithm that needs to be interchangeable (e.g., bubble sort, quicksort, mergesort), we can use the Strategy Pattern to encapsulate each sorting algorithm as a separate strategy.
  • Searching Algorithms: Similar to sorting, we can have different search algorithms (linear search, binary search, etc.) encapsulated as strategies.
  • Payment Gateways: In a payment processing system, you might have different payment gateways (e.g., PayPal, Stripe, Square). Using the Strategy Pattern, we can encapsulate the logic for each payment gateway as a separate strategy, allowing the system to switch between different payment methods easily.
  • Compression Algorithms: If we are working with a system that involves compressing and decompressing data, different compression algorithms (e.g., gzip, zlib, lzma) can be implemented as separate strategies.
  • Image Processing: In an image processing application, we may have different algorithms for image filtering, resizing, or color adjustments. Each of these algorithms can be implemented as a separate strategy, allowing the user to choose the desired image processing technique.
  • Game Development: In game development, strategies can be used for different behaviors of characters or enemies. For example, we might have different strategies for the movement, attack, and defense of game entities.
  • Network Communication Protocols: In networking, different communication protocols (e.g., TCP, UDP) can be implemented as separate strategies. This allows the system to switch between protocols without affecting the overall communication logic.
  • Logging Strategies: Logging mechanisms in software can have different strategies for output (e.g., console logging, file logging, database logging). The Strategy Pattern can be applied to switch between logging strategies dynamically.

Advantage of Strategy Method design Pattern in Java

Flexibility and Extensibility

  • Strategies are encapsulated in separate classes, making it easy to add, remove, or modify strategies without affecting the client code.
  • New algorithms can be introduced without altering the existing codebase, promoting an open/closed principle.

Clean Code and Separation of Concerns

  • The pattern promotes clean code by separating the concerns of the algorithm from the client code.
  • Each strategy is encapsulated in its own class, leading to better code organization and maintenance.

Promotes Code Reusability

  • Strategies can be reused in different contexts, as they are independent and encapsulated in their own classes.
  • Once a new strategy is implemented, it can be easily reused in other parts of the application without duplicating code.

Easy Testing

  • Testing is simplified because each strategy is a separate class. It is easier to write unit tests for individual strategies, ensuring that each algorithm behaves as expected.

Dynamic Runtime Behavior

  • The pattern allows clients to switch between different strategies at runtime. This flexibility is valuable when the algorithm to be used is determined dynamically during the program’s execution.

Encourages Single Responsibility Principle

  • Each strategy class has a single responsibility: encapsulating a specific algorithm. This aligns with the Single Responsibility Principle, making the codebase more maintainable and understandable.

Decouples Algorithms from Context

  • The pattern decouples the algorithm implementation from the client code. The client interacts with the context, which delegates the algorithm to the selected strategy. This separation improves code clarity and reduces dependencies.

Facilitates Better Design Patterns

  • The Strategy pattern is often used in conjunction with other design patterns, such as the Factory Method pattern or the Singleton pattern, to create more sophisticated and flexible designs.

In summary, the Strategy design pattern provides a way to define a family of algorithms, encapsulate each algorithm, and make them interchangeable. This flexibility, along with improved code organization and maintainability, makes it a valuable pattern in software design.

Disadvantage of Strategy Method Design Pattern in Java

While the Strategy design pattern offers several advantages, it’s essential to consider potential disadvantages as well. Here are some drawbacks associated with the Strategy pattern:

  • Increased Number of Classes: Implementing the Strategy pattern often involves creating a separate class for each strategy. In situations where there are numerous strategies, this can lead to an increased number of classes, potentially making the codebase more complex.
  • Potential for Overhead: The use of multiple strategy classes can introduce a level of indirection, which might lead to some performance overhead. The client has to instantiate and manage strategy objects, and there could be additional function call overhead when delegating to different strategies.
  • Complexity for Small Projects: For small projects or situations where the number of algorithms is limited and unlikely to change, applying the Strategy pattern might introduce unnecessary complexity. In such cases, a simpler solution could be more appropriate.
  • Potential for Misuse: If not applied judiciously, the Strategy pattern can be misused, leading to an overly complex and convoluted design. It’s essential to assess whether the flexibility provided by the pattern is genuinely needed for the specific requirements of the system.
  • Learning Curve for Developers: Developers who are new to the codebase might find it challenging to understand the relationships between the context and the various strategy classes. This can be mitigated with good documentation and code comments.
  • Inherent Complexity of Algorithms: The Strategy pattern doesn’t inherently simplify the complexity of individual algorithms. If the algorithms themselves are complex, the benefits of encapsulation and interchangeability might be outweighed by the inherent intricacy of the strategies.
  • Potential for Code Duplication: If there are common elements across different strategies, there might be a risk of code duplication. Careful design and consideration of shared components are necessary to avoid redundancy.
  • Difficulty in Choosing Appropriate Strategies: In some cases, determining the most suitable strategy at runtime can be a non-trivial task. The logic for choosing strategies might become complex, especially in situations where multiple factors influence the decision.


Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads