Strategy Pattern
Strategy Pattern
Section titled “Strategy Pattern”The Strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. The algorithm can vary independently from clients that use it.
When to Use
Section titled “When to Use”- Multiple algorithms for the same operation (sorting, compression, payment processing)
- Selecting behaviour at runtime based on configuration or user choice
- Replacing conditionals (
if/switch) with polymorphism - Making behaviour easily testable by swapping in test doubles
C# Implementation
Section titled “C# Implementation”// Strategy interfacepublic interface IDiscountStrategy{ decimal Calculate(decimal orderTotal, Customer customer);}
// Concrete strategiespublic class NoDiscount : IDiscountStrategy{ public decimal Calculate(decimal total, Customer customer) => 0;}
public class LoyaltyDiscount : IDiscountStrategy{ public decimal Calculate(decimal total, Customer customer) { return customer.LoyaltyPoints > 1000 ? total * 0.10m : total * 0.05m; }}
public class SeasonalDiscount : IDiscountStrategy{ private readonly decimal _percentage; public SeasonalDiscount(decimal percentage) => _percentage = percentage;
public decimal Calculate(decimal total, Customer customer) => total * (_percentage / 100);}
public class BulkDiscount : IDiscountStrategy{ public decimal Calculate(decimal total, Customer customer) => total > 500 ? total * 0.15m : 0;}
// Context — uses the strategypublic class OrderPricer{ private readonly IDiscountStrategy _discount;
public OrderPricer(IDiscountStrategy discount) { _discount = discount; }
public decimal CalculateTotal(decimal subtotal, Customer customer) { var discount = _discount.Calculate(subtotal, customer); return subtotal - discount; }}
// Usage — pick strategy at runtimeIDiscountStrategy strategy = customer.IsPremium ? new LoyaltyDiscount() : new SeasonalDiscount(10);
var pricer = new OrderPricer(strategy);var total = pricer.CalculateTotal(250.00m, customer);TypeScript — Strategy with Functions
Section titled “TypeScript — Strategy with Functions”In TypeScript, strategies can be plain functions rather than classes:
type SortStrategy<T> = (a: T, b: T) => number;
function sortBy<T>(items: T[], strategy: SortStrategy<T>): T[] { return [...items].sort(strategy);}
const products: Product[] = [...];
// Different strategiesconst byPrice: SortStrategy<Product> = (a, b) => a.price - b.price;const byName: SortStrategy<Product> = (a, b) => a.name.localeCompare(b.name);const byRating: SortStrategy<Product> = (a, b) => b.rating - a.rating;
sortBy(products, byPrice); // cheapest firstsortBy(products, byName); // alphabeticalsortBy(products, byRating); // highest rated firstReplacing Switch Statements
Section titled “Replacing Switch Statements”Before Strategy:
public decimal CalculateShipping(string method, decimal weight){ return method switch { "standard" => weight * 0.50m, "express" => weight * 1.50m + 5, "overnight" => weight * 3.00m + 15, _ => throw new ArgumentException($"Unknown method: {method}") };}After Strategy:
public interface IShippingStrategy{ decimal Calculate(decimal weight);}
public class StandardShipping : IShippingStrategy{ public decimal Calculate(decimal weight) => weight * 0.50m;}
public class ExpressShipping : IShippingStrategy{ public decimal Calculate(decimal weight) => weight * 1.50m + 5;}
// Add new methods without touching existing code — Open/Closed Principlepublic class OvernightShipping : IShippingStrategy{ public decimal Calculate(decimal weight) => weight * 3.00m + 15;}Strategy with DI in .NET
Section titled “Strategy with DI in .NET”// Register strategiesservices.AddKeyedScoped<IShippingStrategy, StandardShipping>("standard");services.AddKeyedScoped<IShippingStrategy, ExpressShipping>("express");services.AddKeyedScoped<IShippingStrategy, OvernightShipping>("overnight");
// Resolve by keypublic class ShippingCalculator( [FromKeyedServices("express")] IShippingStrategy strategy){ public decimal Calculate(decimal weight) => strategy.Calculate(weight);}Strategy vs State Pattern
Section titled “Strategy vs State Pattern”The Strategy pattern is selected externally — the context doesn’t change strategy during its own lifecycle.
The State pattern transitions between states internally — the object changes its own behaviour based on its current state.