The Goal

Change the algorithm an object uses to do its job.

Example

Imagine you are building a checkout system. The system knows how to calculate the total, but the behavior of how the money actually moves depends on what the user selects (Credit Card, PayPal, or Crypto).

  1. The Strategy interface (The interchangeable behavior)
public interface PaymentStrategy {
    void pay(double amount);
}
  1. Different Behaviors
public class CreditCardStrategy implements PaymentStrategy {
    public void pay(double amount) { System.out.println("Paid " + amount + " via Credit Card."); }
}
public class PayPalStrategy implements PaymentStrategy {
    public void pay(double amount) { System.out.println("Paid " + amount + " via PayPal."); }
}
  1. The Context (The object doing the work)
public class CheckoutCart {
    private PaymentStrategy paymentStrategy; // We hold a behavior

    // We SWAP the behavior at runtime
    public void setPaymentStrategy(PaymentStrategy strategy) {
        this.paymentStrategy = strategy;
    }

    public void checkout(double totalAmount) {
        // The BEHAVIORAL handoff
        paymentStrategy.pay(totalAmount);
    }
}

Setter vs Constructor

In the CheckoutCart example, a setter method (setPaymentStrategy) is used instead of a constructor. It’s written this way to emphasize that the Strategy pattern allows you to swap out behaviors on the fly at runtime.

However, it is incredibly common to pass the Strategy directly through the constructor.

NOTE

In fact, if you are building enterprise applications and using dependency injection (like in Spring Boot), passing the strategy via the constructor is the standard approach.

Here is what the CheckoutCart looks like when we use a constructor, which makes the initial setup look completely identical to the Decorator:

public class CheckoutCart {
    // Marked as final because we set it once in the constructor
    private final PaymentStrategy paymentStrategy; 

    // Here is the constructor injection!
    public CheckoutCart(PaymentStrategy paymentStrategy) {
        this.paymentStrategy = paymentStrategy;
    }
    public void checkout(double totalAmount) {
        // We still delegate the behavior to the injected strategy
        paymentStrategy.pay(totalAmount);
    }
}

Why this is Behavioral

  1. Notice how CheckoutCart doesn’t wrap the payment strategy to add features to it.
  2. It delegates the _action_ of paying to the strategy.
  3. We aren’t building a bigger, more complex object; we are just swapping out the “brain” that handles the logic.