«Statique» comme indice sémantique sur l'apatridie?

11

J'ai récemment entrepris une refactorisation d'un projet de taille moyenne en Java pour revenir en arrière et ajouter des tests unitaires. Quand j'ai réalisé à quel point c'était pénible de se moquer des singletons et de la statique, j'ai finalement «compris» ce que j'avais lu à leur sujet tout ce temps. (Je fais partie de ces personnes qui ont besoin d'apprendre de leur expérience. Oh bien.)

Donc, maintenant que j'utilise Spring pour créer les objets et les câbler, je me débarrasse des staticmots clés à gauche et à droite. (Si je pouvais potentiellement m'en moquer, ce n'est pas vraiment statique dans le même sens que Math.abs (), n'est-ce pas?) Le fait est que j'avais pris l'habitude d'utiliser staticpour indiquer qu'une méthode ne reposait pas sur n'importe quel état d'objet. Par exemple:

//Before
import com.thirdparty.ThirdPartyLibrary.Thingy;
public class ThirdPartyLibraryWrapper {
    public static Thingy newThingy(InputType input) {
         new Thingy.Builder().withInput(input).alwaysFrobnicate().build();
    }
}

//called as...
ThirdPartyLibraryWrapper.newThingy(input);
//After
public class ThirdPartyFactory {
    public Thingy newThingy(InputType input) {
         new Thingy.Builder().withInput(input).alwaysFrobnicate().build();
    }
}

//called as...
thirdPartyFactoryInstance.newThingy(input);

Alors, c'est là que ça devient délicat. J'ai aimé l'ancienne façon parce que la lettre majuscule m'a dit que, tout comme Math.sin (x), ThirdPartyLibraryWrapper.newThingy (x) faisait la même chose de la même manière à chaque fois. Il n'y a aucun état d'objet pour changer la façon dont l'objet fait ce que je lui demande de faire. Voici quelques réponses possibles que j'envisage.

  • Personne d'autre ne ressent cela, alors il y a quelque chose qui ne va pas chez moi. Peut-être que je n'ai pas vraiment intériorisé la façon OO de faire les choses! Peut-être que j'écris en Java mais que je pense en FORTRAN ou quelque chose comme ça. (Ce qui serait impressionnant puisque je n'ai jamais écrit FORTRAN.)
  • J'utilise peut-être la statique comme une sorte de proxy pour l'immuabilité à des fins de raisonnement sur le code. Cela étant dit, quels indices devrais- je avoir dans mon code pour que quelqu'un vienne le maintenir afin de savoir ce qui est et ce qui ne l'est pas?
  • Peut-être que cela devrait venir gratuitement si je choisis de bonnes métaphores d'objet? eg thingyWrapperne sonne pas comme s'il avait un état indépendant de l'enveloppé Thingyqui peut lui-même être mutable. De même, un thingyFactoryson comme il devrait être immuable mais pourrait avoir différentes stratégies qui sont choisies parmi à la création.
leoger
la source

Réponses:

12

J'ai aimé l'ancienne façon parce que la lettre majuscule m'a dit que, tout comme Math.sin (x), ThirdPartyLibraryWrapper.newThingy (x) faisait la même chose de la même manière à chaque fois. Il n'y a aucun état d'objet pour changer la façon dont l'objet fait ce que je lui demande de faire.

C'est la bonne façon de procéder, oui. Cela dit, staticne confère aucune garantie d'immuabilité ou de persistance de l'État. Que vous l'utilisiez comme suggestion pour de telles garanties est logique, mais ce n'est en aucun cas assuré.

Considérer ce qui suit:

public static class Logger
{
    public static int LogLevel { get; set; }

    public static void Log(string message, int logLevel)
    {
        if (logLevel >= LogLevel)
        {
            // logs the message, but only if it is important enough.
        }
    }
}

Cette classe contient non seulement l'état, mais le comportement de la Logger.Log()méthode change lorsque cet état est modifié. C'est un exercice parfaitement légitime d'un staticmodèle de classe, mais il n'a aucune des garanties sémantiques que vous proposez.

Robert Harvey
la source
Je vous ai donné la coche parce que vous présentez un contrepoint utile à la base de mon intuition, mais j'aimerais quand même avoir votre avis sur la façon dont je devrais représenter ce contrat / attente concernant le manque d'état et le manque d'effets secondaires via le codage conventions.
leoger
1
Normalement, ces attentes sont incluses dans la documentation de la méthode; il en va de même pour la sécurité des threads.
Robert Harvey
5

À mon avis, ce que vous dites - toutes les fonctions statiques doivent être nullipotentes - est une bonne façon OO d'écrire du code dans la plupart des cas, mais je ne pense pas que lorsque vous regardez une fonction statique, vous devez automatiquement supposer qu'elle n'a pas d'effets secondaires . La situation peut être plus complexe; prenez par exemple la fonction Class.forName(String)qui semble être sans état mais elle charge en fait une classe dans la mémoire et finalement instancie les champs statiques / lance l'initialisation statique; c'est un exemple de fonction idempotente (les appels éventuels après le premier ne font aucune différence) mais ce n'est pas une fonction pure (on ne peut pas dire qu'elle n'a pas d'effets secondaires). C'est également un bon choix, mais il peut y avoir des cas où trois appels distincts à la même fonction donnent trois résultats différents; par exemple si vous appelezThread.currentThread() dans une application multi-thread, il n'y a aucune garantie que vous recevrez le même thread à chaque fois.

Cela étant dit, quels indices devrais-je avoir dans mon code pour que quelqu'un vienne le maintenir afin de savoir ce qui est et ce qui ne l'est pas?

Une bonne solution est de documenter (Javadoc par exemple); aussi, du nom de la fonction et de ce qu'elle fait parfois, on peut déduire que la fonction statique est une fonction pure. Par exemple, il n'y a aucune raison pour que quelqu'un pense que Assert.assertTrue(boolean)JUnit changerait quelque part l'état. D'un autre côté, quand une fonction comme System.clearProperty(String)est appelée, il est assez évident qu'il y aura des effets secondaires.

m3th0dman
la source
Quelle est la différence entre "nullipotent" et "apatride"?
Pacerier
@Pacerier Il ne devrait pas y avoir de différence.
m3th0dman
4

Cela étant dit, quels indices devrais-je avoir dans mon code pour que quelqu'un vienne le maintenir afin de savoir ce qui est et ce qui ne l'est pas?

Vous pouvez les rendre statiques. FxCop dans le monde C # vous poussera à rendre les choses statiques qui ne font référence à aucune variable membre. C'est la bonne chose à faire (généralement).

Quand j'ai réalisé à quel point c'était pénible de se moquer des singletons et de la statique, j'ai finalement «compris» ce que j'avais lu à leur sujet tout ce temps.

Les déplacer tous vers des instances n'est peut-être pas la bonne approche. À la place, dissociez les dépendances des méthodes statiques des classes qui les utilisent. Vous pouvez tester les méthodes statiques isolément, puis remplacer la dépendance dans les consommateurs lorsqu'ils doivent être testés.

Telastyn
la source
Pouvez-vous me donner un exemple de "découplage des dépendances sur les méthodes statiques"? Ce n'est pas clair pour moi ce que vous activez. J'ai déjà caché les détails de l'implémentation dans une méthode statique. À la fin de la journée, la classe qui l' utilise doit atteindre cette fonctionnalité. Êtes-vous en train de dire que je devrais créer un objet instance qui a des wrappers fins pour toutes les méthodes statiques?
leoger
1
@leoger - la classe qui utilise la méthode statique n'a pas besoin d'atteindre la fonctionnalité si vous testez cette classe, sinon vous ne vous moqueriez pas de la méthode statique.
Telastyn