<aside> đ đ Category: Behavioral Pattern
</aside>
Strategy is a behavioral design pattern that lets you define a family of algorithms, put each of them into a separate class, and make their objects interchangeable. It enables selecting an algorithm's behavior at runtime instead of implementing a single algorithm directly.
<aside> âšī¸ When to use: When you want to use different variants of an algorithm within an object and be able to switch from one algorithm to another during runtime. When you have a lot of similar classes that only differ in the way they execute some behavior.
</aside>
You're building a navigation app. Initially, it only builds routes for cars. Later, you add walking, cycling, and public transport options. Using if-else statements for each type creates messy code that's hard to maintain and test.
Extract all different algorithms into separate classes called strategies. The original class (context) stores a reference to one of the strategies and delegates work to the strategy object instead of executing it directly.
// Strategy interface
public interface IPaymentStrategy
{
void Pay(decimal amount);
}
// Concrete Strategies
public class CreditCardPayment : IPaymentStrategy
{
private string _cardNumber;
private string _cvv;
public CreditCardPayment(string cardNumber, string cvv)
{
_cardNumber = cardNumber;
_cvv = cvv;
}
public void Pay(decimal amount)
{
Console.WriteLine($"Paid ${amount} using Credit Card ending in {_cardNumber.Substring(_cardNumber.Length - 4)}");
}
}
public class PayPalPayment : IPaymentStrategy
{
private string _email;
public PayPalPayment(string email)
{
_email = email;
}
public void Pay(decimal amount)
{
Console.WriteLine($"Paid ${amount} using PayPal account {_email}");
}
}
public class BitcoinPayment : IPaymentStrategy
{
private string _walletAddress;
public BitcoinPayment(string walletAddress)
{
_walletAddress = walletAddress;
}
public void Pay(decimal amount)
{
Console.WriteLine($"Paid ${amount} using Bitcoin to wallet {_walletAddress}");
}
}
public class ShoppingCart
{
private IPaymentStrategy _paymentStrategy;
private decimal _total;
public void SetPaymentStrategy(IPaymentStrategy strategy)
{
_paymentStrategy = strategy;
}
public void AddItem(decimal price)
{
_total += price;
}
public void Checkout()
{
if (_paymentStrategy == null)
{
throw new InvalidOperationException("Payment strategy not set");
}
_paymentStrategy.Pay(_total);
_total = 0;
}
}
// Usage
var cart = new ShoppingCart();
cart.AddItem(29.99m);
cart.AddItem(49.99m);
// Pay with credit card
cart.SetPaymentStrategy(new CreditCardPayment("1234567812345678", "123"));
cart.Checkout();
// Or pay with PayPal
cart.AddItem(99.99m);
cart.SetPaymentStrategy(new PayPalPayment("user@example.com"));
cart.Checkout();
from abc import ABC, abstractmethod
# Strategy interface
class PaymentStrategy(ABC):
@abstractmethod
def pay(self, amount: float):
pass
# Concrete Strategies
class CreditCardPayment(PaymentStrategy):
def __init__(self, card_number: str, cvv: str):
self.card_number = card_number
self.cvv = cvv
def pay(self, amount: float):
print(f"Paid ${amount} using Credit Card ending in {self.card_number[-4:]}")
class PayPalPayment(PaymentStrategy):
def __init__(self, email: str):
self.email = email
def pay(self, amount: float):
print(f"Paid ${amount} using PayPal account {self.email}")
class BitcoinPayment(PaymentStrategy):
def __init__(self, wallet_address: str):
self.wallet_address = wallet_address
def pay(self, amount: float):
print(f"Paid ${amount} using Bitcoin to wallet {self.wallet_address}")
# Context
class ShoppingCart:
def __init__(self):
self._payment_strategy = None
self._total = 0.0
def set_payment_strategy(self, strategy: PaymentStrategy):
self._payment_strategy = strategy
def add_item(self, price: float):
self._total += price
def checkout(self):
if not self._payment_strategy:
raise ValueError("Payment strategy not set")
self._payment_strategy.pay(self._total)
self._total = 0.0
# Usage
cart = ShoppingCart()
cart.add_item(29.99)
cart.add_item(49.99)
cart.set_payment_strategy(CreditCardPayment("1234567812345678", "123"))
cart.checkout()
public interface ISortStrategy
{
void Sort(List<int> list);
}
public class QuickSort : ISortStrategy
{
public void Sort(List<int> list)
{
Console.WriteLine("Sorting using QuickSort");
list.Sort(); // Simplified
}
}
public class MergeSort : ISortStrategy
{
public void Sort(List<int> list)
{
Console.WriteLine("Sorting using MergeSort");
// Merge sort implementation
}
}
public class BubbleSort : ISortStrategy
{
public void Sort(List<int> list)
{
Console.WriteLine("Sorting using BubbleSort");
// Bubble sort implementation
}
}
public class Sorter
{
private ISortStrategy _strategy;
public void SetStrategy(ISortStrategy strategy)
{
_strategy = strategy;
}
public void Sort(List<int> list)
{
_strategy.Sort(list);
}
}
// Usage - choose strategy based on data size
var sorter = new Sorter();
var data = new List<int> { 5, 2, 8, 1, 9 };
if (data.Count < 10)
sorter.SetStrategy(new BubbleSort());
else if (data.Count < 1000)
sorter.SetStrategy(new QuickSort());
else
sorter.SetStrategy(new MergeSort());
sorter.Sort(data);