Strategy Interface
The Strategy pattern defines a family of algorithms and makes them interchangeable.
Core Structure
┌──────────────────────────┐
│ Context │
├──────────────────────────┤
│ - strategy: Strategy │
├──────────────────────────┤
│ + setStrategy(Strategy) │
│ + executeStrategy() │
└──────────┬───────────────┘
│ uses
▼
┌──────────────────────────┐
│ «interface» │
│ Strategy │
├──────────────────────────┤
│ + execute(data): Result │
└──────────┬───────────────┘
│ implemented by
┌─────┴──────┐
│ │
┌────┴─────┐ ┌───┴──────┐
│ConcreteA │ │ConcreteB │
└──────────┘ └──────────┘
Strategy Interface
// Strategy interface defines the algorithm contract
public interface SortStrategy {
void sort(int[] array);
String getName();
}
// Concrete strategies implement different algorithms
public class BubbleSort implements SortStrategy {
@Override
public void sort(int[] array) {
// Bubble sort implementation
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array.length - i - 1; j++) {
if (array[j] > array[j + 1]) {
swap(array, j, j + 1);
}
}
}
}
@Override
public String getName() { return "Bubble Sort"; }
}
public class QuickSort implements SortStrategy {
@Override
public void sort(int[] array) {
// QuickSort implementation
quickSort(array, 0, array.length - 1);
}
@Override
public String getName() { return "Quick Sort"; }
}
When to Define Strategy Interface
- Multiple algorithms for the same problem
- Algorithm selection at runtime
- Eliminating conditional statements
- Making algorithms interchangeable
Strategy Interface Guidelines
- Small interface: 1-3 methods ideally
- Clear contract: What the strategy does
- No side effects: Strategy shouldn't modify context
- Stateless when possible: Strategies without state are easier to reuse
Concrete Strategies
Concrete strategies implement the strategy interface with specific algorithms.
Payment Processing Example
public interface PaymentStrategy {
PaymentResult pay(Money amount, PaymentDetails details);
boolean supportsRefund();
}
public class CreditCardStrategy implements PaymentStrategy {
private final String cardNumber;
private final String cvv;
public CreditCardStrategy(String cardNumber, String cvv) {
this.cardNumber = cardNumber;
this.cvv = cvv;
}
@Override
public PaymentResult pay(Money amount, PaymentDetails details) {
// Credit card processing logic
return new PaymentResult(true, "CC-" + UUID.randomUUID());
}
@Override
public boolean supportsRefund() { return true; }
}
public class PayPalStrategy implements PaymentStrategy {
private final String email;
public PayPalStrategy(String email) {
this.email = email;
}
@Override
public PaymentResult pay(Money amount, PaymentDetails details) {
// PayPal processing logic
return new PaymentResult(true, "PP-" + UUID.randomUUID());
}
@Override
public boolean supportsRefund() { return true; }
}
public class CryptoStrategy implements PaymentStrategy {
@Override
public PaymentResult pay(Money amount, PaymentDetails details) {
// Cryptocurrency processing logic
return new PaymentResult(true, "BTC-" + UUID.randomUUID());
}
@Override
public boolean supportsRefund() { return false; }
}
Pricing Strategies
public interface PricingStrategy {
Money calculatePrice(Order order);
}
public class RegularPricing implements PricingStrategy {
public Money calculatePrice(Order order) {
return order.getSubtotal();
}
}
public class PremiumPricing implements PricingStrategy {
private final double discountRate;
public PremiumPricing(double discountRate) {
this.discountRate = discountRate;
}
public Money calculatePrice(Order order) {
return order.getSubtotal().multiply(1 - discountRate);
}
}
public class BulkPricing implements PricingStrategy {
public Money calculatePrice(Order order) {
if (order.getItemCount() >= 10) {
return order.getSubtotal().multiply(0.8); // 20% off
}
return order.getSubtotal();
}
}
Strategy Selection
| Strategy | Use When |
|---|---|
| RegularPricing | Default pricing |
| PremiumPricing | Subscription users |
| BulkPricing | Large orders |
| PromoPricing | Active promotion |
Context Class
The Context class uses the strategy and allows swapping strategies at runtime.
Context Implementation
public class PriceCalculator {
private PricingStrategy strategy;
// Constructor injection
public PriceCalculator(PricingStrategy strategy) {
this.strategy = strategy;
}
// Setter injection for runtime swapping
public void setStrategy(PricingStrategy strategy) {
this.strategy = strategy;
}
public Money calculate(Order order) {
return strategy.calculatePrice(order);
}
}
Context with Strategy Selection
public class PaymentService {
private final Map<PaymentType, PaymentStrategy> strategies;
public PaymentService() {
strategies = new HashMap<>();
strategies.put(PaymentType.CREDIT_CARD, new CreditCardStrategy());
strategies.put(PaymentType.PAYPAL, new PayPalStrategy());
strategies.put(PaymentType.CRYPTO, new CryptoStrategy());
}
public PaymentResult processPayment(Order order, PaymentType type) {
PaymentStrategy strategy = strategies.get(type);
if (strategy == null) {
throw new UnsupportedPaymentType(type);
}
return strategy.pay(order.getTotal(), order.getPaymentDetails());
}
}
Runtime Strategy Swapping
// Start with regular pricing
PriceCalculator calculator = new PriceCalculator(new RegularPricing());
Money price = calculator.calculate(order); // Regular price
// User becomes premium member
calculator.setStrategy(new PremiumPricing(0.1));
price = calculator.calculate(order); // 10% discount
// During bulk promotion
calculator.setStrategy(new BulkPricing());
price = calculator.calculate(order); // Bulk discount if applicable
Strategy with Dependency Injection
// Spring-style injection
@Service
public class OrderService {
private final PricingStrategy pricingStrategy;
@Autowired
public OrderService(PricingStrategy pricingStrategy) {
this.pricingStrategy = pricingStrategy;
}
public Order calculateOrder(Order order) {
Money total = pricingStrategy.calculatePrice(order);
order.setTotal(total);
return order;
}
}
// Different beans for different strategies
@Configuration
public class PricingConfig {
@Bean
@Profile("default")
public PricingStrategy regularPricing() {
return new RegularPricing();
}
@Bean
@Profile("premium")
public PricingStrategy premiumPricing() {
return new PremiumPricing(0.15);
}
}
Benefits of Strategy Pattern
- Open-Closed Principle: Add new strategies without modifying context
- Eliminates conditionals: No if/else chains for algorithm selection
- Runtime flexibility: Swap algorithms during execution
- Testability: Test each strategy in isolation
- Reusability: Strategies can be shared across contexts
Practice Problems
Design a scalable Strategy Pattern system. Cover high-level architecture, data model, and API design.
Solution
// Complete system design:
// - Functional + Non-functional requirements
// - Capacity estimation
// - Data model (SQL/NoSQL choice)
// - API endpoints
// - Component architecture
// - Scaling strategy
// - Monitoring & reliabilityHow would you scale Strategy Pattern to handle 10x the current load? Identify bottlenecks and solutions.
Solution
// Scaling approach:
// 1. Load balancing
// 2. Database sharding/replication
// 3. Cache layer (Redis)
// 4. CDN for static assets
// 5. Async processing (queues)
// 6. Microservices decompositionAnalyze potential failure modes for Strategy Pattern and design mitigation strategies.
Solution
// Failure mitigation:
// 1. Redundancy (multi-AZ)
// 2. Circuit breakers
// 3. Retry with backoff
// 4. Dead letter queues
// 5. Health checks
// 6. Graceful degradationQuiz
1. What does the Strategy pattern allow?
2. How is the strategy injected into the context?
3. What principle does Strategy pattern help follow?
4. What replaces if/else chains with Strategy pattern?
5. When should you use Strategy pattern?
Flashcards
Question
What is the Strategy pattern?
Click to reveal answer
Answer
Defines a family of algorithms, encapsulates each one, and makes them interchangeable. Allows algorithm selection at runtime through composition.
Question
What are the components of Strategy pattern?
Click to reveal answer
Answer
Strategy interface (defines algorithm), Concrete strategies (implement algorithms), Context (uses strategy via composition).
Question
How is strategy swapped at runtime?
Click to reveal answer
Answer
Through setter injection on the context: context.setStrategy(newStrategy). Or through a strategy registry/map.
Question
What does Strategy pattern replace?
Click to reveal answer
Answer
Complex if/else or switch chains for algorithm selection. Instead of conditionals, use polymorphism through strategy objects.
Question
What principle does Strategy follow?
Click to reveal answer
Answer
Open-Closed Principle: add new algorithms by adding new strategy classes, without modifying existing context code.
Revision Notes
Key Takeaways
- 1.Strategy pattern defines a family of interchangeable algorithms
- 2.Strategies are injected into context via constructor or setter
- 3.Strategy enables Open-Closed Principle: add algorithms without modifying context
- 4.Replaces complex if/else chains with polymorphic strategy objects
- 5.Runtime strategy swapping enables flexible behavior
Interview Tips
- •Show Strategy pattern when discussing algorithm flexibility
- •Explain how Strategy eliminates conditional logic
- •Demonstrate runtime strategy swapping in your design
- •Discuss how Strategy supports testing in isolation
Cheat Sheet
Strategy Pattern - Cheat Sheet
Structure:
Context ──uses──▶ Strategy (interface)
│
┌────────┴────────┐
ConcreteA ConcreteB
Key Points:
- Strategy interface defines algorithm
- Concrete strategies implement algorithms
- Context uses strategy via composition
- Swapped at runtime via setter
Benefits:
- OCP: Add new strategies without modifying context
- No if/else chains
- Runtime flexibility
- Testable in isolation
Use Cases:
- Payment processing
- Pricing algorithms
- Sorting algorithms
- Notification delivery
- Route calculation
Implementation:
- Define strategy interface
- Implement concrete strategies
- Context holds strategy reference
- Inject strategy via constructor/setter