web-dev-qa-db-fra.com

Injection de dépendance vs méthodes statiques

J'ai eu une discussion intéressante aujourd'hui avec un autre développeur sur la façon d'aborder une classe avec une méthode qui accepte une chaîne et génère une chaîne.

Imaginez quelque chose comme ce qui suit qui est complètement composé à des fins d'exemple

public string GetStringPart(string input)
{ 
   //Some input validation which is removed for clarity

   if(input.Length > 5)
        return input.Substring(0,1);

   if(input.Substring(0,1) == "B")
        return input.Substring(0,3);

   return string.empty;
}

Une fonction qui a une logique basée sur son entrée de chaîne est ajoutée à un projet à l'aide de DI et a un conteneur DI en place. Souhaitez-vous ajouter cette nouvelle classe avec une interface et l'injecter si nécessaire, ou en feriez-vous une classe statique? Quels sont les avantages et les inconvénients de chacun? Pourquoi voudriez-vous (ou non) que ce quelque chose soit utilisé avec l'injection de constructeur plutôt que simplement accessible lorsque cela est requis n'importe où?.

21
James

Il n'y a aucune raison pour que cela soit injecté. C'est juste une fonction, elle n'a pas de dépendances, alors appelez-la simplement. Il peut même être statique si vous le souhaitez car il semble pur. On peut écrire des tests unitaires contre cela sans difficulté. S'il est utilisé dans d'autres classes, des tests unitaires peuvent toujours être écrits.

Il n'est pas nécessaire de faire abstraction des fonctions sans dépendances, c'est exagéré.

Si cela devient plus complexe, il est peut-être justifié de passer une interface à un constructeur ou à une méthode. Mais je n'irais pas dans cette direction à moins d'avoir une logique complexe de GetStringPart basée sur l'emplacement, etc.

27
Jon Raynor

Voici pourquoi

class DOSClient {
    OrderParser orderParser;
    string orderCode;

    DOSClient(OrderParser orderParser, string ordercode) { 
        this.orderParser = orderParser; 
        this.ordercode = ordercode;
    }
    void DisplayOrderCode() {
        Console.Write( "Prefix: " + orderParser.GetStringPart(ordercode) ); 
        ...
    }
}

class GUIClient {
    OrderParser orderParser;
    string orderCode;
    GUI gui;

    GUIClient(OrderParser orderParser, string ordercode, GUI gui) { 
        this.orderParser = orderParser; 
        this.ordercode = ordercode;
        this.gui = gui;
    }

    void DisplayOrderCode() {
        gui.Prefix( orderParser.GetStringPart(ordercode) ); 
        ...
    }
}

class OrderParserUS : IOrderParser {

    public string GetStringPart(string input)
    { 
        //Some input validation which is removed for clarity

        if(input.Length > 5)
            return input.Substring(0,1);

        if(input.Substring(0,1) == "B")
            return input.Substring(0,3);

        return string.empty;
    }
}

class OrderParserEU : IOrderParser {

    public string GetStringPart(string input)
    { 
        //Some input validation which is removed for clarity

        if(input.Length > 6)
            return input.Substring(0,1);

        if(input.Substring(0,1) == "#")
            return input.Substring(0,3);

        return string.empty;
    }
}

Si vous aviez opté pour une méthode statique, il n'y aurait aucun moyen de modifier le comportement de GetStringPart sans détruire l'ancien comportement ou le polluer avec une logique conditionnelle. Il est vrai que les statiques sont de mauvais globaux déguisés, mais le fait qu'ils désactivent le polymorphisme est ma principale plainte à leur sujet. Les méthodes statiques ne sont pas de première classe dans les langages OOP. En donnant à la méthode un objet dans lequel vivre, même sans état, nous rendons la méthode portable. Son comportement peut être transmis comme le valeur d'une variable.

Ici, j'ai imaginé un système qui doit se comporter légèrement différemment lorsqu'il est déployé en Europe puis aux États-Unis. Au lieu de cela, forcez l'un ou l'autre système à contenir du code uniquement nécessaire à l'autre, nous pouvons changer le comportement en contrôlant quel objet d'analyse syntaxique est injecté dans les clients. Cela nous permet de contenir la propagation du détail de la région. Il permet également d'ajouter facilement OrderParserCanada sans avoir à toucher les analyseurs existants.

Si cela ne signifie rien pour vous, il n'y a vraiment pas de bon argument pour cela.

BTW, GetStringPart est un nom terrible.

12
candied_orange