In real applications, situations frequently arise where the same type of operation must be performed in different ways, depending on the context:
-
calculating the final price (standard discount, promotional, VIP)
-
processing payments (card, PayPal, bank transfer)
-
different validations for the same form
-
different sorting or filtering algorithms
A naive approach quickly leads to:
if (type == A) { ... }
else if (type == B) { ... }
else if (type == C) { ... }
👉 Strategy Pattern elegantly solves this problem.
What is Strategy Pattern?
Strategy Pattern is a behavioral design pattern that:
-
defines a family of algorithms
-
encapsulates them in separate classes
-
makes them interchangeable at runtime
-
eliminates if / switch statements from client code
📌 In short:
behavior changes without modifying the class that uses it.
Pattern structure
Strategy Pattern has 3 main components:
-
Strategy (interface) – defines the algorithm contract
-
ConcreteStrategy – different implementations of the algorithm
-
Context – uses a strategy without knowing implementation details
Client → Context → Strategy
↑
ConcreteStrategyA
ConcreteStrategyB
Practical example: calculating the final price
Scenario
We have an online store that applies different pricing calculation strategies:
-
standard price
-
percentage discount
-
fixed discount
1️⃣ Strategy Interface
public interface IPricingStrategy
{
decimal CalculatePrice(decimal basePrice);
}
This defines the algorithm that can vary.
2️⃣ Concrete implementations (Concrete Strategies)
Standard price (no discount)
public class StandardPricingStrategy : IPricingStrategy
{
public decimal CalculatePrice(decimal basePrice)
{
return basePrice;
}
}
Percentage discount
public class PercentageDiscountStrategy : IPricingStrategy
{
private readonly decimal _percentage;
public PercentageDiscountStrategy(decimal percentage)
{
_percentage = percentage;
}
public decimal CalculatePrice(decimal basePrice)
{
return basePrice - (basePrice * _percentage);
}
}
Fixed discount
public class FixedDiscountStrategy : IPricingStrategy
{
private readonly decimal _discountAmount;
public FixedDiscountStrategy(decimal discountAmount)
{
_discountAmount = discountAmount;
}
public decimal CalculatePrice(decimal basePrice)
{
return Math.Max(0, basePrice - _discountAmount);
}
}
3️⃣ The Context
The Context does not know which concrete strategy it uses, only the interface.
public class PriceCalculator
{
private IPricingStrategy _pricingStrategy;
public PriceCalculator(IPricingStrategy pricingStrategy)
{
_pricingStrategy = pricingStrategy;
}
public void SetStrategy(IPricingStrategy pricingStrategy)
{
_pricingStrategy = pricingStrategy;
}
public decimal Calculate(decimal basePrice)
{
return _pricingStrategy.CalculatePrice(basePrice);
}
}
4️⃣ Usage – changing the strategy at runtime
var calculator = new PriceCalculator(
new StandardPricingStrategy()
);
decimal basePrice = 100m;
Console.WriteLine(calculator.Calculate(basePrice)); // 100
calculator.SetStrategy(
new PercentageDiscountStrategy(0.1m)
);
Console.WriteLine(calculator.Calculate(basePrice)); // 90
calculator.SetStrategy(
new FixedDiscountStrategy(25)
);
Console.WriteLine(calculator.Calculate(basePrice)); // 75
🎯 No if, no switch, no changes in PriceCalculator.
Integration with Dependency Injection (ASP.NET / .NET)
In real applications, strategies can be resolved from the DI container:
services.AddTransient<StandardPricingStrategy>();
services.AddTransient<PercentageDiscountStrategy>();
services.AddTransient<FixedDiscountStrategy>();
Or dynamically selected based on context (user, request, config).
Advantages
✅ eliminates conditional code
✅ respects the Open/Closed principle
✅ easy to test (mock the interface)
✅ behavior changeable at runtime
✅ cleaner and extensible code
Disadvantages
⚠️ larger number of classes
⚠️ can be overkill for very simple logic
⚠️ client must know which strategy to choose
When to use Strategy Pattern?
✔ when you have multiple algorithms for the same operation
✔ when you want to avoid complex if / switch statements
✔ when behavior must be changed dynamically
✔ when working with variable business rules
Conclusion
Strategy Pattern is one of the most used and useful structures in modern .NET applications.
It helps you separate what you do from how you do it, keeping code flexible, testable, and easy to extend.
☕ If you want clean and scalable applications, Strategy Pattern must be in your basic arsenal.