Archives Wild Code School
ASP.Net

ASP.NET: Injection de dépendances

Quête 3 sur 7

Objectifs

  • Comprendre le principe de couplage
  • Comprendre le principe d'inversion de contrôle
  • Mettre en place l'injection de dépendances

Couplage

On parle de couplage quand il y a interaction entre des composants logiciels. Si une classe appelle une instance d'une autre classe, il y a "couplage". Parfois le couplage est plus ou moins fort, par exemple : si tu crées une instance d'une autre classe, le couplage est très fort.

Pour mieux comprendre cette notion, voici un exemple abstrait :

1. Couplage fort

Tu es le patron d'un site de tests de jeux vidéos et tu cherches des pigistes.

Dans un premier temps, tu décides d'embaucher des salariés pigistes sur ton besoin actuel, qui est de réaliser des tests sous la forme d’articles.

Après un certain temps, le marché des sites de tests de jeux vidéos évolue et tu voudrais déplacer ton activité vers une plate-forme de test au format vidéo.

Tes salariés ne sont, à juste titre, pas content : ils sont compétents dans l’écriture d’articles, pas pour tourner des vidéos !

Dans cet exemple, les tests réalisés sont fortement liés aux testeurs : on parle de couplage fort.

2. Couplage faible

Maintenant, imaginons que tu es toujours le patron d'un site de tests de jeux vidéos, sauf que cette fois, tu vas passer par un chasseur de tête pour trouver des testeurs en freelances.

Le chasseur de tête va te demander de rédiger un contrat sur les comportements attendus d'un testeur :

  • la rédaction envoie le jeu
  • le testeur réalise le test
  • la rédaction reçoit le test

Dans un premier temps, tu vas demander au chasseur de tête de te trouver des pigistes correspondants aux critères : ils vont réaliser des articles.

Quelques temps plus tard, tu décides que les tests sont réalisés sous format vidéo : tant que le contrat prévu est respecté, le chasseur de tête trouvera quelqu'un correspondant aux critères.

Dans cet exemple, les tests réalisés sont faiblement liés aux testeurs : on parle de couplage faible.

3. En conclusion

Dans le cas d'un couplage fort, tu rencontreras les problèmes suivants : • le code peut être difficile à lire, surtout lorsqu'il y a beaucoup d'interdépendances : on parle de code spaghetti • les composants seront difficilement réutilisables, car liés les uns aux autres • les composants seront difficilement testables, car il sera nécessaire de définir en amont toutes les dépendances (qui elles mêmes pourront apporter leurs lots d'erreurs)

Lors de la réalisation d'un couplage faible, tous les inconvénients soulevés plus tôt sont ainsi gommés.

↗
Polymorphisme

Explication de la notion de polymorphisme

fr.wikipedia.org

Inversion de contrôle

Dans le cadre d'un projet informatique, l'inversion de contrôle (IoC) est un principe de programmation qui consiste à déléguer des tâches au framework, par exemple la création d'instances. Le but de l’IoC est de réduire les dépendances entre les composants et donc pousser au maximum le couplage faible.

L'IoC est parfois surnommée the Hollywood Principle, car elle illustre une phrase célèbre dans le milieu du cinéma américain : « Don't call us, we'll call you ».

Pour mettre en place l'IoC, il faut réaliser des contrats (interfaces), afin de réaliser une abstraction du comportement de chaque composant.

Le comportement réel sera quand à lui réalisé dans l'implémentation de ces interfaces.

Il devient alors possible de déléguer au framework la mise en relation de ces composants

↗
Inversion de contrôle
fr.wikipedia.org

Injection de dépendances

Un service

Un service est tout simplement une classe mise à disposition par de l'injection de dépendances. Peut-être qualifié de service n'importe quelle classe injectée dans un conteneur.

ASP.Net dispose de son propre système d'injection de dépendances. Ce système diffère énormément de beaucoup d'autres par le fait que la configuration n'est pas faite dans un langage descriptif (yaml, json, xml ...) mais avec du code.

L'injection de dépendances se configure dans la classe Program générée automatiquement lorsqu'un projet ASP.Net est crée:

cs
public class Program
{
    public static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);

        [...]

        builder.Services.AddScoped<Interface, Implementation>(); // Register Implementation as Interface

        [...]

        var app = builder.Build();

        [...]
    }
}

Plusieurs éléments sont à analyser dans le code ci-dessus:

  • builder - Le builder qui va initialiser l'application permet d'accéder à builder.Services qui est une collection qui contient tous les services injectés dans l'application
  • builder.Service.AddScoped<Interface, Implementation>() - Est la méthode qui permet de faire correspondre la classe Implementation à l'interface Interface.

Ainsi, le code ci-dessus rend disponible la classe Implementation à toute l'application mais elle sera injectée comme une interface.

Je m'illustre:

Services/ISalaryCalculator.cs

cs
namespace Example.Services
{
    public interface ISalaryCalculator
    {
        Double CalculateSalary(Double yearlyAmount);
    }
}

Dans ce fichier est définie l'interface ISalaryCalculator qui expose une méthode CalculateSalary. Cette méthode permet de calculer un salaire.

Services/MonthlySalaryCalculator.cs

cs
namespace Example.Services
{
    public class MonthlySalaryCalculator : ISalaryCalculator
    {
        public Double CalculateSalary(Double yearlyAmount)
        {
            return yearlyAmount / 12;
        }
    }
}

Dans ce fichier est définie une classe MonthlySalaryCalculator. Cette classe a pour responsabilité de transformer un salaire annuel en salaire mensuel.

Controllers/SalaryController.cs

cs
namespace Example.Controllers
{
    [Route("api/[controller]")]
    public class SalaryController
    {
        private ISalaryCalculator _calculator;

        public SalaryController(ISalaryCalculator injectedCalculator)
        {
            _calculator = injectedCalculator;
        }

        [HttpGet("{yearlyAmount}")]
        public Double Get(Double yearlyAmount)
        {
            Double calculatedSalary = _calculator.CalculateSalary(yearlyAmount);
            return calculatedSalary;
        }
    }
}

Dans ce fichier est défini un contrôleur qui expose une route Api/Salary/{yearlyAmount}.

Program.cs

cs
namespace IdentityTest
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);

            [...]

            // Below, MonthlySalaryCalculator is injected as ISalaryCalculator
            builder.Services.AddScoped<ISalaryCalculator, MonthlySalaryCalculator>();

            [...]

            var app = builder.Build();

            [...]
        }
    }
}

Je t'enjoins de lire entièrement la ressource suivante, pour clarifier le concept avant de passer au challenge:

↗
Injection de dépendances ASP.Net

Documentation officielle sur l'injection de dépendances et l'inversion de contrôle en ASP.Net

learn.microsoft.com

Challenge

A partir de l'exemple précédent, injecte une classe QuarterlySalaryCalculator injectée en tant que ISalaryCalculator par ASP.Net. La classe QuarterlySalaryCalculator transforme un salaire annuel en salaire trimestriel.

Critères de validation

  • Les fichiers Controllers/SalaryController.cs, Services/MonthlySalaryCalculator.cs, Services/ISalaryCalculator.cs et Program.cs sont présents à l'identique dans la solution
  • Un fichier Services/QuarterlySalaryCalculator.cs définit une classe implémentant l'interface ISalaryCalculator
  • L'implémentation de la méthode CalculateSalary de la classe QuarterlySalaryCalculator transforme un salaire annuel en salaire trimestriel
  • Dans le fichier Program.cs, un appel à la méthode AddScoped injecte QuarterlySalaryCalculator en tant que ISalaryCalculator
  • La route Api/Salary/{montantAnnuel} retourne un salaire trimestriel