Strategy Design Pattern in Java
Select an algorithm’s behavior at runtime by making a family of algorithms interchangeable.
What is Strategy?
The Strategy design pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. This lets the algorithm vary independently from the clients that use it. In a payment processing system, the payment context works with different payment gateways, while each gateway encapsulates its own payment-processing algorithm.
Beginner-friendly Java example
Focus on the roles in the example first. The pattern becomes easier when you can identify the sender, receiver, context, state, or strategy involved.
// Step 1: Define the PaymentStrategy interface
interface PaymentStrategy {
void processPayment(double amount);
}
// Step 2: Implement concrete payment strategies
class PayPalStrategy implements PaymentStrategy {
private String email;
private String password;
public PayPalStrategy(String email, String password) {
this.email = email;
this.password = password;
}
@Override
public void processPayment(double amount) {
// Process payment using PayPal API
System.out.println("Payment processed via PayPal: $" + amount);
}
}
class StripeStrategy implements PaymentStrategy {
private String apiKey;
public StripeStrategy(String apiKey) {
this.apiKey = apiKey;
}
@Override
public void processPayment(double amount) {
// Process payment using Stripe API
System.out.println("Payment processed via Stripe: $" + amount);
}
}
// Step 3: Define the PaymentContext
class PaymentContext {
private PaymentStrategy paymentStrategy;
public PaymentContext(PaymentStrategy paymentStrategy) {
this.paymentStrategy = paymentStrategy;
}
public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
this.paymentStrategy = paymentStrategy;
}
public void processPayment(double amount) {
paymentStrategy.processPayment(amount);
}
}
// Step 4: Test the implementation
public class Main {
public static void main(String[] args) {
PaymentStrategy paypalStrategy = new PayPalStrategy("paypal@example.com", "password");
PaymentContext paymentContext = new PaymentContext(paypalStrategy);
paymentContext.processPayment(100.0);
PaymentStrategy stripeStrategy = new StripeStrategy("stripe-api-key");
paymentContext.setPaymentStrategy(stripeStrategy);
paymentContext.processPayment(150.0);
}
}Benefits and trade-offs
Benefits
- Keeps responsibilities focused.
- Reduces conditional and tightly coupled code.
- Makes behavior easier to extend and test.
Trade-offs
- Can introduce extra objects and interfaces.
- Too many small classes can make simple logic harder to follow.
Real-time use cases
- Payment gateway selection
- Shipping price rules
- Tax calculation strategies
- Discount and pricing rules
- Compression algorithms
Key takeawaySelect an algorithm’s behavior at runtime by making a family of algorithms interchangeable. Use it when behavior or communication is changing faster than the objects themselves.
