The Strategy Design Pattern is a behavioral design pattern that enables selecting an algorithm's behavior at runtime. It defines a family of algorithms, encapsulates each one, and makes them interchangeable. The pattern lets the algorithm vary independently from the clients that use it. This is particularly useful when you want to swap or change algorithms without affecting the client code.
Key Concepts:
Strategy Interface: Declares an interface common to all supported algorithms.
Concrete Strategies: Implement different variations of the algorithm.
Context: Uses a reference to the Strategy interface to call the algorithm defined by a Concrete Strategy.
This pattern is useful when you have multiple ways of performing an operation, and the decision of which to use can change dynamically.
Example Scenario: Payment Methods
Let's consider an online shopping platform where a user can choose different payment methods (e.g., PayPal, CreditCard, etc.). The strategy pattern allows switching between different payment strategies without altering the core logic.
Java Example:
1. Strategy Interface:
public interface PaymentStrategy {
void pay(int amount);
String getPaymentType();
}
2. Concrete Strategy Classes:
public class CreditCardPayment implements PaymentStrategy {
private String name;
private String cardNumber;
public CreditCardPayment(String name, String cardNumber) {
this.name = name;
this.cardNumber = cardNumber;
}
@Override
public void pay(int amount) {
System.out.println(amount + " paid with Credit Card.");
}
@Override
public String getPaymentType() {
return "Credit Card";
}
}
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 using PayPal.");
}
@Override
public String getPaymentType() {
return "PayPal";
}
}
3. Context Class:
public class ShoppingCart {
private PaymentStrategy paymentStrategy;
public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
this.paymentStrategy = paymentStrategy;
}
public void checkout(int amount) {
paymentStrategy.pay(amount);
}
}
4. Client Code:
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
ShoppingCart cart = new ShoppingCart();
Scanner scanner = new Scanner(System.in);
List<PaymentStrategy> paymentStrategies = new ArrayList<>();
paymentStrategies.add(new CreditCardPayment("John Doe", "1234-5678-9012-3456"));
paymentStrategies.add(new PayPalPayment("john@example.com"));
System.out.println("Available Payment Methods:");
for (int i = 0; i < paymentStrategies.size(); i++) {
System.out.println((i + 1) + ". " + paymentStrategies.get(i).getPaymentType());
}
System.out.print("Choose a payment method by number: ");
int choice = scanner.nextInt();
if (choice > 0 && choice <= paymentStrategies.size()) {
cart.setPaymentStrategy(paymentStrategies.get(choice - 1));
System.out.print("Enter the amount to pay: ");
int amount = scanner.nextInt();
cart.checkout(amount);
} else {
System.out.println("Invalid choice.");
}
scanner.close();
}
}
Output:
Available Payment Methods:
1. Credit Card
2. PayPal
Choose a payment method by number: 1
Enter the amount to pay: 12
12 paid with Credit Card.
Benefits of the Strategy Pattern:
Open/Closed Principle: New strategies can be added without modifying the client code.
Avoids Conditional Statements: You avoid multiple conditional if/else or switch blocks to choose algorithms.
Runtime Flexibility: Strategies can be changed at runtime.
When to Use:
- When multiple related classes differ only in their behavior (algorithm).
- When you need different variants of an algorithm and want to switch them dynamically.
- When you want to avoid complex conditional logic.
This pattern is particularly useful when the selection of an algorithm needs to happen dynamically, and it simplifies code management for algorithms that may evolve independently.
Top comments (1)
Useful article on the Strategy DP.