Qu'est-ce que l'injection de dépendances (DI) en C# et pourquoi en a-t-on besoin ?
\n\nL'injection de dépendances (Dependency Injection) est un patron de conception qui permet de rendre le code plus flexible, testable et maintenable. Au lieu que la classe crée elle-même les objets dont elle dépend, ces objets sont "injectés" de l'extérieur. C'est l'un des principes clés à la base du développement moderne en C# et .NET.
\n\nImaginez que vous écrivez une application pour envoyer des notifications par email. Si votre classe NotificationService crée directement une instance de SmtpClient, alors lors d'un changement de serveur de messagerie ou de la nécessité d'utiliser un autre mode d'envoi (par exemple, via une API), vous devrez réécrire le code. L'injection de dépendances résout ce problème.
Le problème sans injection de dépendances
\n\nConsidérons un exemple typique de mauvais code, où la dépendance est créée à l'intérieur de la classe :
\n\npublic class EmailService\n{\n private SmtpClient _smtpClient;\n\n public EmailService()\n {\n // Liaison rigide à une implémentation spécifique\n _smtpClient = new SmtpClient("smtp.gmail.com", 587);\n }\n\n public void SendEmail(string to, string subject, string body)\n {\n // Logique d'envoi\n }\n}\n\npublic class NotificationService\n{\n private EmailService _emailService;\n\n public NotificationService()\n {\n _emailService = new EmailService(); // Liaison rigide\n }\n\n public void NotifyUser(string email)\n {\n _emailService.SendEmail(email, "Notification", "Bonjour !");\n }\n}\n\nInconvénients de cette approche :
\n- \n
- Couplage fort (tight coupling) : Les classes
NotificationServiceetEmailServicesont fortement liées l'une à l'autre. \n - Difficulté de test : Il est impossible de remplacer
EmailServicepar un objet mock lors des tests unitaires. \n - Difficulté d'extension : Si vous devez ajouter des notifications par SMS, vous devrez modifier la logique à l'intérieur de
NotificationService. \n
Implémentation de l'injection de dépendances en C#
\n\nL'injection de dépendances résout ces problèmes en utilisant trois approches principales : l'injection par constructeur, par propriété et par méthode. La plus courante et recommandée est l'injection par constructeur (Constructor Injection).
\n\nÉtape 1. Créer une interface
\nDéfinissons d'abord une abstraction (interface) qui décrit le contrat pour l'envoi de messages :
\n\npublic interface IMessageSender\n{\n void Send(string to, string message);\n}\n\nÉtape 2. Implémenter des services concrets
\nCréons maintenant plusieurs implémentations de cette interface :
\n\npublic class EmailSender : IMessageSender\n{\n public void Send(string to, string message)\n {\n Console.WriteLine($"Envoi d'email à {to}: {message}");\n }\n}\n\npublic class SmsSender : IMessageSender\n{\n public void Send(string to, string message)\n {\n Console.WriteLine($"Envoi de SMS à {to}: {message}");\n }\n}\n\nÉtape 3. Appliquer l'injection de dépendances par constructeur
\nMaintenant, la classe NotificationService ne crée pas elle-même les dépendances, mais les reçoit via le constructeur :
public class NotificationService\n{\n private readonly IMessageSender _messageSender;\n\n // Injection de dépendance via le constructeur\n public NotificationService(IMessageSender messageSender)\n {\n _messageSender = messageSender;\n }\n\n public void Notify(string userContact, string message)\n {\n _messageSender.Send(userContact, message);\n }\n}\n\nNous pouvons désormais facilement changer les implémentations :
\n\n// Utilisation d'Email\nvar emailSender = new EmailSender();\nvar emailNotifier = new NotificationService(emailSender);\nemailNotifier.Notify("user@example.com", "Bonjour par email !");\n\n// Utilisation de SMS\nvar sms