How to Implement Stategy Design Pattern in Java

The Strategy Pattern is a behavioral design pattern that allows a family of algorithms to be defined and encapsulated within a class hierarchy. The pattern enables an algorithm to vary independently from clients that use it by allowing the behavior (or “strategy”) to be selected at runtime.

Key Components of the Strategy Pattern:

  1. Strategy Interface: Defines an interface common to all supported algorithms. The context uses this interface to call the algorithm defined by a concrete strategy.
  2. Concrete Strategies: Implement the algorithm using the Strategy interface.
  3. Context: Maintains a reference to a Strategy object and calls its algorithm when required.

Example: Implementing Strategy Pattern in Java

We’ll implement a simple payment system where users can choose different payment strategies like Credit Card or PayPal to process payments.

Step 1: Define the Strategy Interface

// PaymentStrategy.java
public interface PaymentStrategy {
    void pay(double amount);
}

Step 2: Create Concrete Strategy Classes

Each concrete strategy implements the PaymentStrategy interface to define specific payment behaviors.

  • CreditCardPayment:
// CreditCardPayment.java
public class CreditCardPayment implements PaymentStrategy {
    private String cardNumber;
    private String cardHolderName;

    public CreditCardPayment(String cardNumber, String cardHolderName) {
        this.cardNumber = cardNumber;
        this.cardHolderName = cardHolderName;
    }

    @Override
    public void pay(double amount) {
        System.out.println(amount + " paid with credit card (" + cardHolderName + ").");
    }
}
  • PayPalPayment:
// PayPalPayment.java
public class PayPalPayment implements PaymentStrategy {
    private String email;

    public PayPalPayment(String email) {
        this.email = email;
    }

    @Override
    public void pay(double amount) {
        System.out.println(amount + " paid using PayPal (" + email + ").");
    }
}

Step 3: Create the Context Class

The Context class (e.g., ShoppingCart) will interact with the PaymentStrategy and delegate the payment to the selected strategy at runtime.

// ShoppingCart.java
import java.util.ArrayList;
import java.util.List;

public class ShoppingCart {
    private List items;
    private PaymentStrategy paymentStrategy;

    public ShoppingCart() {
        this.items = new ArrayList<>();
    }

    public void addItem(double price) {
        items.add(price);
    }

    public double calculateTotal() {
        return items.stream().mapToDouble(Double::doubleValue).sum();
    }

    public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
        this.paymentStrategy = paymentStrategy;
    }

    public void checkout() {
        double total = calculateTotal();
        if (paymentStrategy == null) {
            System.out.println("Please select a payment method.");
        } else {
            paymentStrategy.pay(total);
        }
    }
}

Step 4: Use the Strategy Pattern in Main Class

// Main.java
public class Main {
    public static void main(String[] args) {
        ShoppingCart cart = new ShoppingCart();

        // Adding items to the cart
        cart.addItem(50.0);
        cart.addItem(100.0);

        // Paying with Credit Card
        PaymentStrategy creditCardPayment = new CreditCardPayment("1234-5678-9012", "John Doe");
        cart.setPaymentStrategy(creditCardPayment);
        cart.checkout();  // Output: 150.0 paid with credit card (John Doe).

        // Paying with PayPal
        PaymentStrategy payPalPayment = new PayPalPayment("john.doe@example.com");
        cart.setPaymentStrategy(payPalPayment);
        cart.checkout();  // Output: 150.0 paid using PayPal (john.doe@example.com).
    }
}

Key Points of the Example:

  1. Strategy Interface: PaymentStrategy defines the pay() method, which all concrete strategies implement.
  2. Concrete Strategies: CreditCardPayment and PayPalPayment implement the specific payment algorithms.
  3. Context: ShoppingCart is the context class that interacts with the PaymentStrategy interface and allows for switching payment strategies dynamically.

Output:

150.0 paid with credit card (John Doe).
150.0 paid using PayPal (john.doe@example.com).

Benefits of the Strategy Pattern:

  • Flexible: Algorithms can be changed dynamically at runtime.
  • Maintainable: Each algorithm is encapsulated in its own class, adhering to the Single Responsibility Principle.
  • Extensible: New strategies can be easily introduced without changing existing code.

When to Use:

  • When you have multiple ways to perform an operation, and you need the flexibility to switch between these methods at runtime.
  • To avoid using many conditional statements like if-else or switch-case when dealing with different behaviors.
+---------------+           +------------------+
|    Client     |           |    Strategy       |
|               |<--------->|  + operation()    |
+---------------+           +------------------+
       |                                |
       v                                v
+---------------+           +------------------+
|    Context    |           | ConcreteStrategyA |
|               |           |  + operation()    |
|  -strategy    |           +------------------+
+---------------+                   ^
       |                             |
       v                             v
                               +------------------+
                               | ConcreteStrategyB |
                               |  + operation()    |
                               +------------------+

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *