Skip to main content

The Strategy Pattern

Category: Behavioral

The Strategy pattern defines a family of algorithms, encapsulates each one into a separate class, and makes their objects interchangeable at runtime.

This pattern lets the algorithm vary independently from the clients that use it. It is heavily used to replace massive if/else or switch statements when making dynamic behavioral choices.


Example: The E-Commerce Cart

Imagine an E-Commerce application. A user has a Shopping Cart, and they want to checkout. They should be able to choose their payment method dynamically at checkout: Credit Card, PayPal, or Crypto.

Instead of writing a giant if(paymentType == "CREDIT_CARD") inside the Cart, we extract the payment algorithms into their own classes!

1. The Strategy Interface

This interface is common to all supported versions of the algorithm.

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

2. Concrete Strategies

These classes implement the PaymentStrategy interface. Each class encapsulates a specific algorithm.

// Strategy 1: Credit Card
public class CreditCardStrategy implements PaymentStrategy {

private String name;
private String cardNumber;

public CreditCardStrategy(String name, String cardNumber) {
this.name = name;
this.cardNumber = cardNumber;
}

@Override
public void pay(int amount) {
System.out.println(amount + " paid with credit/debit card: " + cardNumber);
}
}

// Strategy 2: PayPal
public class PaypalStrategy implements PaymentStrategy {

private String emailId;

public PaypalStrategy(String email) {
this.emailId = email;
}

@Override
public void pay(int amount) {
System.out.println(amount + " paid using PayPal account: " + emailId);
}
}

3. The Context Class (The Shopping Cart)

The Context class maintains a reference to a Strategy object. It does not know exactly how the payment is processed; it just calls the pay() method on the strategy it was given.

public class ShoppingCart {

private int totalAmount = 0;

// Assuming items are added and totalAmount is calculated here...
public void addItemCost(int price) {
this.totalAmount += price;
}

// The Context delegates the work to the Strategy object!
public void checkout(PaymentStrategy paymentMethod) {
paymentMethod.pay(totalAmount);
}
}

4. Client Code

At runtime, the client creates a specific strategy object and passes it to the context.

public class LabStrategy1 {

public static void main(String[] args) {
ShoppingCart cart = new ShoppingCart();
cart.addItemCost(10);
cart.addItemCost(40);

// 1. User decides to pay with PayPal
cart.checkout(new PaypalStrategy("myemail@example.com"));

// 2. User decides to pay with Credit Card
cart.checkout(new CreditCardStrategy("John Doe", "1234-5678-9012-3456"));
}
}

[!TIP] Why is this so powerful? If you need to add "Apple Pay" tomorrow, you do not need to modify the ShoppingCart class at all! You simply create an ApplePayStrategy class and pass it in. This perfectly follows the Open/Closed Principle (Open for extension, closed for modification).