Injection de dépendance vs méthodes statiques

21

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ù?

James
la source
1
@Ewan Pour les méthodes d'assistance qui n'ont aucune utilité pour l'abstraction. Exemple: Apache FileUtils.
Walfrat
3
@Ewan: les méthodes statiques sans effets secondaires sont les meilleures méthodes, car elles sont simples à comprendre et simples à tester.
JacquesB
1
mais ils rendent impossible le test unitaire des choses qui dépendent d'eux
Ewan
3
@Ewan Eh, pas vraiment. Math.Max ​​() est-il mauvais parce qu'il est statique? Si vous testez votre méthode statique et qu'elle fonctionne, vous pouvez l'utiliser en toute sécurité sur vos autres méthodes sans problème. Si le statique échoue, son test le rattrapera.
T. Sar - Rétablir Monica
1
si «statique» ne garantissait aucun effet secondaire, je pourrais voir l'argument. mais ce n'est pas le cas.
Ewan

Réponses:

25

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 d'abstraire les 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 voie à moins d'avoir une GetStringPartlogique complexe basée sur l'emplacement, etc.

Jon Raynor
la source
2
Ceci est la bonne réponse! Dommage que la réponse au culte du fret ait été acceptée.
JacquesB
3
que la fonction n'a pas de dépendances n'est pas le point. le problème est quand d'autres choses dépendent de la fonction et y sont étroitement liées
Ewan
2
Et si la fonction change en 6 mois et n'est pas si pure, mais elle est déjà utilisée dans beaucoup d'endroits. Ce n'est pas extensible en tant que statique et ce n'est pas non plus quelque chose qui peut être isolé de la consommation de tests unitaires de classes. S'il y a même une légère chance de changement, j'irais l'injecter. Cela dit, je suis ouvert à l'écoute d'arguments opposés, c'est le but de ce post. Quels sont les inconvénients de cette injection et les avantages d'une statique?
James
1
D'une manière générale, les personnes qui discutent de l'injection de dépendance sur ce site Web n'ont pas besoin d'une injection de dépendance. D'où le biais vers une complexité inutile.
Frank Hileman
2
@James Si la fonction devient moins pure, vous faites quelque chose de mal et la fonction n'était probablement pas bien définie depuis le début. Le calcul de l'aire d'un carré ne devrait pas soudainement nécessiter un appel à la base de données ou déployer une mise à jour visuelle pour l'interface utilisateur. Pouvez-vous imaginer un scénario où une méthode de type "Substring" devient soudainement impure?
T. Sar - Rétablir Monica
12

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 changer le comportement de GetStringPartsans 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 POO. 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 la 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 l'ordre d'analyse de l'objet qui 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, GetStringPartest un nom terrible.

candied_orange
la source
* Cringe au style de code entre crochets * Je suppose que vous êtes un programmeur Java essayant d'écrire du code C #? Il faut en connaître un, je suppose. :)
Neil
La question n'est pas spécifique à une langue, plus sur le design. Mon problème avec les méthodes statiques est qu'elles empêchent l'isolement du code pour les tests. Le développeur à qui je parlais pense que je compte trop sur la DI et que parfois les méthodes d'extension / méthodes statiques sont pertinentes. Je pense que peut-être dans de rares cas, mais pas à des fins générales. Discussion en cours que j'estimais être une bonne discussion à approfondir. Je suis également d'accord avec votre argument en faveur du polymorphisme.
James
Les tests sont une raison mais il y en a plus. Je n'aime pas trop blâmer cela sur les tests, car cela commence simplement à critiquer les tests. Le simple fait est que les programmeurs procéduraux ne l'aiment pas si vous ne programmez pas de manière procédurale.
candied_orange
Ne pourriez-vous pas simplement dire static getStringPartEU()? Votre exemple n'a de sens que s'il existe d'autres méthodes dans cette classe qui nécessitent également le traitement spécialisé de l'UE et qu'elles doivent être traitées comme une seule unité.
Robert Harvey
2
Vous plaidez définitivement en faveur d'une complexité inutile. Tout peut avoir de plus en plus de code ajouté. Les choses qui doivent changer devraient être faciles à modifier, mais vous ne devriez pas essayer de prédire ce qui devrait changer à l'avenir. Les champs statiques peuvent être identiques aux variables globales, mais les méthodes statiques peuvent être pures, le meilleur type de méthode possible.
Frank Hileman