Le Strategy Pattern est l'un des patrons de conception comportementaux les plus utiles en programmation orientée objet. Il permet d'encapsuler une famille d'algorithmes interchangeables et de les rendre indépendants des clients qui les utilisent. Dans cet article, nous allons explorer en détail ce pattern essentiel et voir comment l'implémenter efficacement en C# et .NET.
Comprendre le Strategy Pattern
Le Strategy Pattern définit une famille d'algorithmes, encapsule chacun d'eux et les rend interchangeables. Il permet de faire varier indépendamment les algorithmes des clients qui les utilisent. Ce pattern est particulièrement utile lorsque :
- Vous avez une famille d'algorithmes similaires
- Vous devez pouvoir changer d'algorithme dynamiquement
- Vous souhaitez isoler la logique algorithmique du code qui l'utilise
Implémentation en C#
Voici un exemple concret d'implémentation du Strategy Pattern pour gérer différentes stratégies de calcul de prix :
// Interface définissant le contrat pour toutes les stratégies
public interface IPricingStrategy
{
decimal CalculatePrice(decimal basePrice);
}
// Implémentation concrète pour le prix standard
public class RegularPricingStrategy : IPricingStrategy
{
public decimal CalculatePrice(decimal basePrice)
{
return basePrice;
}
}
// Implémentation pour les prix soldés
public class DiscountPricingStrategy : IPricingStrategy
{
private readonly decimal _discountPercentage;
public DiscountPricingStrategy(decimal discountPercentage)
{
_discountPercentage = discountPercentage;
}
public decimal CalculatePrice(decimal basePrice)
{
return basePrice (1 - _discountPercentage / 100);
}
}
// Contexte qui utilise la stratégie
public class PriceCalculator
{
private IPricingStrategy _pricingStrategy;
public PriceCalculator(IPricingStrategy strategy)
{
_pricingStrategy = strategy;
}
public void SetStrategy(IPricingStrategy strategy)
{
_pricingStrategy = strategy;
}
public decimal CalculateFinalPrice(decimal basePrice)
{
return _pricingStrategy.CalculatePrice(basePrice);
}
}
Utilisation et Tests
Voici comment utiliser et tester ce pattern :
[Fact]
public void TestPricingStrategies()
{
// Arrange
var calculator = new PriceCalculator(new RegularPricingStrategy());
var basePrice = 100m;
// Act & Assert - Prix régulier
var regularPrice = calculator.CalculateFinalPrice(basePrice);
Assert.Equal(100m, regularPrice);
// Act & Assert - Prix soldé
calculator.SetStrategy(new DiscountPricingStrategy(20));
var discountedPrice = calculator.CalculateFinalPrice(basePrice);
Assert.Equal(80m, discountedPrice);
}
Bonnes Pratiques
- Gardez les stratégies immutables quand c'est possible
- Utilisez l'injection de dépendances pour gérer les stratégies
- Documentez clairement le comportement de chaque stratégie
- Évitez les dépendances externes dans les stratégies
Intégration avec ASP.NET Core
Voici comment intégrer le pattern dans une application ASP.NET Core :
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped();
services.AddScoped();
}
}
[ApiController]
[Route("[controller]")]
public class PricingController : ControllerBase
{
private readonly PriceCalculator _calculator;
public PricingController(PriceCalculator calculator)
{
_calculator = calculator;
}
[HttpGet("{price}")]
public ActionResult GetPrice(decimal price)
{
return _calculator.CalculateFinalPrice(price);
}
}
Considérations de Performance
Pour optimiser les performances :
- Utilisez le pooling d'objets pour les stratégies fréquemment utilisées
- Évitez de créer de nouvelles instances de stratégies inutilement
- Mettez en cache les résultats des calculs coûteux
Gestion des Erreurs
Implémentez une gestion d'erreurs robuste :
public class PriceCalculator
{
private IPricingStrategy _pricingStrategy;
public decimal CalculateFinalPrice(decimal basePrice)
{
try
{
if (basePrice < 0)
throw new ArgumentException("Le prix de base ne peut pas être négatif");
return _pricingStrategy?.CalculatePrice(basePrice)
?? throw new InvalidOperationException("Aucune stratégie de prix n'est définie");
}
catch (Exception ex)
{
// Log l'erreur
throw new PriceCalculationException("Erreur lors du calcul du prix", ex);
}
}
}
Conclusion
Le Strategy Pattern est un outil puissant pour gérer des familles d'algorithmes interchangeables. Il permet de :
- Découpler les algorithmes de leur utilisation
- Faciliter l'ajout de nouveaux comportements
- Améliorer la maintenabilité du code
- Respecter le principe Open/Closed
En suivant les bonnes pratiques et en comprenant les cas d'usage appropriés, vous pourrez tirer le meilleur parti de ce pattern dans vos applications .NET.