web-dev-qa-db-fra.com

Avons-nous besoin d'interfaces pour l'injection de dépendance?

J'ai une application ASP.NET Core. L'application a peu de classes d'assistance qui fonctionnent. Chaque classe a une méthode de signature différente. Je vois beaucoup d’exemples de base .net en ligne qui créent une interface pour chaque classe, puis enregistrent les types avec le cadre DI. Par exemple

 public interface IStorage
 {
    Task Download(string file);
 }

 public class Storage
 {
    public Task Download(string file)
    {
    }
 }

 public interface IOcr
 {
     Task Process();
 }

 public class Ocr:IOcr
 {
    public Task Process()
    {

    }
 }

Fondamentalement, pour chaque interface, il n'y a qu'une seule classe. Puis-je enregistrer ces types avec DI comme

 services.AddScoped<IStorage, Storage>();
 services.AddScoped<IOcr,Ocr>();

Mais je peux enregistrer le type sans avoir d'interfaces alors les interfaces ici ont l'air redondantes. par exemple

 services.AddScoped<Storage>();
 services.AddScoped<Ocr>();

Alors, ai-je vraiment besoin d'interfaces?

9
LP13

Non, vous n'avez pas besoin de interfaces pour l'injection de dépendance. Mais vous devriez les utiliser!

Comme vous l'avez remarqué, vous pouvez enregistrer des types concrets dans la collection de services et ASP.NET Core les injectera parfaitement dans vos classes. Le seul avantage que vous obtenez par rapport à la création d'instances avec new Storage() est/ gestion de la durée de vie du service (transitoire par rapport au domaine par opposition au singleton).

Ce n'est cependant qu'une partie du pouvoir d'utiliser l'ID. Comme @DavidG l'a souligné, la principale raison pour laquelle les interfaces sont si souvent associées à DI est à cause des tests. Faire en sorte que vos classes dépendent d’interfaces (abstractions) au lieu d’autres classes concrètes les rend beaucoup plus faciles à tester.

Pour vos tests, vous pouvez créer une MockStorage qui implémente IStorage et votre classe de consommateurs ne devrait pas être en mesure de faire la différence. Ou, vous pouvez utiliser un framework moqueur pour créer facilement une IStorage simulée. Faire la même chose avec des classes concrètes est beaucoup plus difficile. Les frameworks moqueurs ne supportent pas les faux cours de béton ou sont très maladroits. Les interfaces sont plus simples et plus propres.

14
Nate Barbettini

Est-ce que ça marche? Oui. Devriez-vous le faire? Non. 

L'injection de dépendance est un outil pour le principe d'inversion de dépendance: https://en.wikipedia.org/wiki/Dependency_inversion_principle

Ou comme décrit dans SOLIDE

il faut "dépendre d'abstractions, [pas] concrétions."

Vous pouvez injectez simplement des classes concrètes et cela fonctionnera. Mais ce n’est pas ce pour quoi DI a été conçue. 

9
MindingData

Je n'essaierai pas de couvrir ce que d'autres ont déjà mentionné, l'utilisation d'interfaces avec DI est souvent la meilleure option. Mais il convient de mentionner que l'utilisation de l'héritage d'objet peut parfois constituer une autre option utile. Donc par exemple:

public class Storage
 {
    public virtual Task Download(string file)
    {
    }
 }


public class DiskStorage: Storage
 {
    public override Task Download(string file)
    {
    }
 }

et en l'enregistrant comme ceci:

services.AddScoped<Storage, DiskStorage>();
1
Ron C

Non, vous n'avez pas besoin d'interfaces. En plus d'injecter des classes ou des interfaces, vous pouvez également injecter des délégués. C'est comparable à l'injection d'une interface avec une méthode.

Exemple:

public delegate int DoMathFunction(int value1, int value2);

public class DependsOnMathFunction
{
    private readonly DoMathFunction _doMath;

    public DependsOnAFunction(DoMathFunction doMath)
    {
        _doMath = doMath;
    }

    public int DoSomethingWithNumbers(int number1, int number2)
    {
        return _doMath(number1, number2);
    }
}

Vous pouvez le faire sans déclarer un délégué, en injectant simplement un Func<Something, Whatever> et cela fonctionnera également. Je me pencherais vers le délégué car il est plus facile de configurer DI. Vous pouvez avoir deux délégués avec la même signature qui remplissent des objectifs différents.

Un avantage à cela est qu'il oriente le code vers la ségrégation des interfaces. Quelqu'un pourrait être tenté d'ajouter une méthode à une interface (et à son implémentation) car elle est déjà injectée quelque part, donc c'est pratique. 

Cela signifie

  • L’interface et l’implémentation acquièrent des responsabilités qu’ils ne devraient probablement pas assumer simplement parce que cela convient à l’instant.
  • La classe qui dépend de l'interface peut également développer sa responsabilité, mais elle est plus difficile à identifier car le nombre de dépendances n'a pas augmenté.
  • Les autres classes finissent par dépendre de l'interface saturée et moins hiérarchisée.

J'ai vu des cas où une dépendance unique finissait par devenir deux ou trois classes totalement distinctes, car il était pratique d'ajouter à une classe et une interface existantes au lieu d'injecter quelque chose de nouveau. Cela a ensuite aidé certaines classes sur le chemin de 2 500 lignes.

Vous ne pouvez pas empêcher quelqu'un de faire ce qu'ils ne devraient pas. Vous ne pouvez pas empêcher quelqu'un de faire en sorte qu'un cours dépende de 10 délégués différents. Mais cela peut définir un modèle qui oriente la croissance future dans la bonne direction et offre une certaine résistance à la multiplication des interfaces et du contrôle des classes.

(Cela ne signifie pas que vous n'utilisez pas d'interfaces. Cela signifie que vous avez des options.)

0
Scott Hannen