Strategy Pattern
Category
Behavioral Design Pattern
Overview
The Strategy Pattern is a behavioral design pattern that enables a class’s behavior to be selected at runtime. Instead of implementing multiple algorithms or behaviors directly in a class, the pattern delegates this functionality to separate classes, each encapsulating a specific behavior or algorithm.
This allows for easy swapping of algorithms, adherence to the Open-Closed Principle (open for extension, closed for modification), and promotes a flexible design.
Key Characteristics
Encapsulation of Algorithms:
Each algorithm is encapsulated in its own class, implementing a common interface.
The client does not need to know which algorithm is being used.
Runtime Flexibility:
The behavior of a class can be changed dynamically by swapping strategies during execution.
Separation of Concerns:
The context class is decoupled from the specific implementation of algorithms, allowing it to focus on its core responsibilities.
Adherence to the Open-Closed Principle:
New strategies can be added without modifying the existing code.
Elimination of Conditional Logic:
Replaces long conditional statements with polymorphism, improving maintainability.
UML Diagram
The UML diagram below illustrates the Strategy Pattern, where the Context class delegates algorithm execution to a Strategy object.
Implementation Walkthrough
Participants
Strategy (Interface):
Defines a common interface for all supported algorithms or behaviors.
Example:
Strategyinterface with aexecute()method.
Concrete Strategies:
Implement the
Strategyinterface to provide specific algorithms or behaviors.Example:
ConcreteStrategyAandConcreteStrategyB.
Context:
Maintains a reference to a
Strategyobject and delegates the execution of behavior to it.Example: A
Contextclass that uses aStrategyobject to perform operations.
Example: Payment Processing System
In a payment system, you might have different payment methods such as credit card, PayPal, and cryptocurrency. Each payment method implements a common interface (PaymentStrategy), and the client (the payment processor) uses the appropriate strategy at runtime.
1. Strategy Interface
/**
* @brief Interface for payment strategies.
*/
public interface PaymentStrategy {
void pay(double amount);
}
2. Concrete Strategies
/**
* @brief Concrete strategy for credit card payments.
*/
public class CreditCardPayment implements PaymentStrategy {
private String cardNumber;
public CreditCardPayment(String cardNumber) {
this.cardNumber = cardNumber;
}
@Override
public void pay(double amount) {
System.out.println("Paid " + amount + " using credit card: " + cardNumber);
}
}
/**
* @brief Concrete strategy for PayPal payments.
*/
public class PayPalPayment implements PaymentStrategy {
private String email;
public PayPalPayment(String email) {
this.email = email;
}
@Override
public void pay(double amount) {
System.out.println("Paid " + amount + " using PayPal: " + email);
}
}
3. Context Class
/**
* @brief Context class for managing payment strategies.
*/
public class PaymentProcessor {
private PaymentStrategy paymentStrategy;
public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
this.paymentStrategy = paymentStrategy;
}
public void processPayment(double amount) {
if (paymentStrategy == null) {
throw new IllegalStateException("Payment strategy not set");
}
paymentStrategy.pay(amount);
}
}
4. Client Code
/**
* @brief Demonstrates the Strategy Pattern.
*/
public class StrategyPatternDemo {
public static void main(String[] args) {
PaymentProcessor processor = new PaymentProcessor();
// Pay using credit card
processor.setPaymentStrategy(new CreditCardPayment("1234-5678-9012-3456"));
processor.processPayment(100.0);
// Pay using PayPal
processor.setPaymentStrategy(new PayPalPayment("user@example.com"));
processor.processPayment(200.0);
}
}
Output
Paid 100.0 using credit card: 1234-5678-9012-3456
Paid 200.0 using PayPal: user@example.com
Applications
When to Use the Strategy Pattern
Multiple Algorithms:
When a class needs to support multiple algorithms or behaviors, and these can vary at runtime.
Example: Sorting algorithms, payment methods.
Avoiding Conditional Logic:
When complex conditional statements are used to determine which behavior to execute.
Example: Using polymorphism to replace
if-elseorswitch-caseconstructs.
Adherence to Open-Closed Principle:
When you need to add new algorithms or behaviors without modifying existing code.
Example: Adding new shipping methods to an e-commerce platform.
Advantages and Disadvantages
Advantages
Flexibility:
Allows dynamic switching of algorithms or behaviors at runtime.
Code Reusability:
Encapsulated strategies are reusable across different contexts.
Testability:
Strategies can be tested independently of the context.
Extensibility:
New strategies can be added without modifying existing code.
Disadvantages
Increased Complexity:
Introduces additional classes for each strategy, increasing codebase size.
Overhead:
Requires careful management of strategy objects, especially if they involve state.
Tight Coupling with Context:
The context must be aware of the
Strategyinterface, which can create dependencies.
Key Takeaways
The Strategy Pattern is ideal for systems requiring multiple interchangeable behaviors.
It simplifies code by encapsulating algorithms into separate classes, improving flexibility and adherence to SOLID principles.
Proper use of the pattern can make systems more modular and extensible, but care should be taken to avoid unnecessary complexity.