Question Pourquoi les variables statiques sont-elles considérées comme mauvaises?


Je suis un programmeur Java qui est nouveau dans le monde de l'entreprise. Récemment j'ai développé une application en utilisant Sensationnel et Java. Tout au long du code que j'ai écrit, j'ai utilisé un bon nombre de statistiques. Les techniciens supérieurs m'ont demandé de réduire le nombre de statiques utilisées. J'ai googlé à peu près la même chose, et je trouve que de nombreux programmeurs sont plutôt contre l'utilisation de variables statiques.

Je trouve les variables statiques plus pratiques à utiliser. Et je présume qu'ils sont efficaces aussi (corrigez-moi si je me trompe), parce que si je devais faire 10 000 appels à une fonction dans une classe, je serais heureux de rendre la méthode statique et d'utiliser une méthode simple. Class.methodCall() sur lui au lieu d'encombrer la mémoire avec 10.000 instances de la classe, non?

De plus, les statistiques réduisent les interdépendances sur les autres parties du code. Ils peuvent agir en tant que détenteurs de l'état parfait. Ajoutant à cela, je trouve que les statiques sont largement mis en œuvre dans certaines langues comme Smalltalk et Scala. Alors pourquoi cette oppression de la statique est-elle répandue chez les programmeurs (en particulier dans le monde de Java)?

PS: veuillez me corriger si mes hypothèses sur la statique sont fausses.


528
2017-08-11 13:14


origine


Réponses:


Les variables statiques représentent l'état global. C'est difficile à raisonner et difficile à tester: si je crée une nouvelle instance d'un objet, je peux raisonner sur son nouvel état dans les tests. Si j'utilise du code qui utilise des variables statiques, il pourrait être dans n'importe quel état - et n'importe quoi pourrait le modifier.

Je pourrais continuer pendant un certain temps, mais le plus grand concept à considérer est que plus la portée de quelque chose est serrée, plus il est facile de raisonner. Nous sommes bons à penser à de petites choses, mais il est difficile de raisonner sur l'état d'un système de millions de lignes s'il n'y a pas de modularité. Cela s'applique à toutes sortes de choses, en passant - pas seulement les variables statiques.


598
2017-08-11 13:18



Ce n'est pas très orienté objet: Une des raisons pour lesquelles la statique pourrait être considérée comme "diabolique" par certaines personnes est qu'elles sont contraires aux paradigme orienté objet. En particulier, il viole le principe selon lequel les données sont encapsulées dans des objets (qui peuvent être étendus, cacher des informations, etc.). Les statiques, de la façon dont vous décrivez leur utilisation, sont essentiellement de les utiliser comme une variable globale pour éviter de traiter des problèmes tels que la portée. Cependant, les variables globales constituent l'une des caractéristiques déterminantes du paradigme de programmation procédurale ou impérative, et non une caractéristique du «bon» code orienté objet. Cela ne veut pas dire que le paradigme procédural est mauvais, mais j'ai l'impression que votre superviseur s'attend à ce que vous écrivez un «bon code orienté objet» et que vous voulez vraiment écrire «bon code de procédure».

Il y a beaucoup de gotchyas en Java quand vous commencez à utiliser des statistiques qui ne sont pas toujours immédiatement évidentes. Par exemple, si vous avez deux copies de votre programme en cours d'exécution sur la même machine virtuelle, vont-elles réduire la valeur de la variable statique et se gêner mutuellement? Ou que se passe-t-il lorsque vous étendez la classe, pouvez-vous remplacer le membre statique? Votre machine virtuelle manque-t-elle de mémoire parce que vous avez un nombre fou de statistiques et que la mémoire ne peut pas être récupérée pour d'autres objets d'instance nécessaires?

Durée de vie de l'objet: De plus, les statistiques ont une durée de vie qui correspond à l'exécution complète du programme. Cela signifie que même lorsque vous avez fini d'utiliser votre classe, la mémoire de toutes ces variables statiques ne peut pas être récupérée. Si, par exemple, vous avez rendu vos variables non statiques, et dans votre fonction main () vous avez créé une seule instance de votre classe, puis avez demandé à votre classe d'exécuter une fonction particulière 10 000 fois, une fois ces 10 000 appels terminés et vous supprimez vos références à l'instance unique, toutes vos variables statiques peuvent être collectées et réutilisées.

Empêche certaines réutilisations: En outre, les méthodes statiques ne peuvent pas être utilisées pour implémenter une interface, de sorte que les méthodes statiques peuvent empêcher certaines fonctionnalités orientées objet d'être utilisables.

Autres options: Si l'efficacité est votre principale préoccupation, il pourrait y avoir d'autres meilleures façons de résoudre le problème de vitesse que de considérer seulement l'avantage de l'invocation qui est habituellement plus rapide que la création. Déterminez si les modificateurs transitoires ou volatils sont nécessaires n'importe où. Pour conserver la possibilité d'être en ligne, une méthode peut être marquée comme finale au lieu de statique. Les paramètres de méthode et d'autres variables peuvent être marqués comme définitifs pour permettre certaines optimisations de compilateur basées sur des hypothèses sur ce qui peut changer ces variables. Un objet d'instance peut être réutilisé plusieurs fois plutôt que de créer une nouvelle instance à chaque fois. Il peut y avoir des commutateurs d'optimisation de complicateur qui devraient être activés pour l'application en général. Peut-être, la conception devrait être mise en place de sorte que les 10.000 exécutions peuvent être multithread et tirer parti des cœurs multiprocesseurs. Si la portabilité n'est pas un problème, peut-être qu'une méthode native vous donnerait une meilleure vitesse que votre statique.

Si, pour une raison quelconque, vous ne voulez pas plusieurs copies d'un objet, le modèle de conception singleton, a des avantages sur les objets statiques, comme la sécurité des threads (en supposant que votre singleton est bien codé), permettant une initialisation paresseuse, garantissant que l'objet a été correctement initialisé lors de son utilisation, sub-classant, avantages à tester et refactoriser votre code, Sans oublier que si vous changez d'avis sur le fait de vouloir seulement une instance d'un objet, il est BEAUCOUP plus facile de supprimer le code pour éviter les instances dupliquées que de refactoriser tout votre code de variable statique pour utiliser des variables d'instance. Je devais le faire avant, ce n'est pas amusant, et vous finissez par devoir éditer beaucoup plus de cours, ce qui augmente le risque d'introduire de nouveaux bugs ... autant mieux de mettre les choses en place "la première fois", même si cela semble avoir ses inconvénients. Pour moi, le re-travail nécessaire si vous décidez de la route que vous avez besoin de plusieurs copies de quelque chose est probablement l'une des raisons les plus impérieuses d'utiliser la statique aussi rarement que possible. Et donc je ne suis pas non plus d'accord avec votre affirmation que la statique réduit les interdépendances, je pense que vous finirez par avoir un code plus couplé si vous avez beaucoup de statiques directement accessibles, plutôt qu'un objet qui "sait comment faire" quelque chose "sur lui-même.


236
2017-08-16 20:20



Le mal est un terme subjectif.

Vous ne contrôlez pas la statique en termes de création et de destruction. Ils vivent à la demande du programme de chargement et de déchargement.

Comme les statistiques résident dans un espace, tous les threads qui souhaitent les utiliser doivent passer par le contrôle d'accès que vous devez gérer. Cela signifie que les programmes sont plus couplés et que ce changement est plus difficile à envisager et à gérer (comme le dit J Skeet). Cela conduit à des problèmes d'isolement de l'impact du changement et affecte ainsi la façon dont les tests sont gérés.

Ce sont les deux principaux problèmes que j'ai avec eux.


83
2017-08-11 13:19



Les états globaux ne sont pas mauvais en soi. Mais nous devons voir votre code pour voir si vous l'avez utilisé correctement. Il est tout à fait possible qu'un débutant abuse des états mondiaux; tout comme il abuserait de toutes les fonctionnalités linguistiques.

Les états globaux sont une nécessité absolue. Nous ne pouvons pas éviter les états globaux. Nous ne pouvons pas éviter de raisonner sur les états mondiaux. - Si nous voulons comprendre notre sémantique d'application.

Les gens qui tentent de se débarrasser des états mondiaux pour cela, finissent inévitablement par un système beaucoup plus complexe - et les états globaux sont toujours là, habilement / idiotement déguisés sous de nombreuses couches d'indirections; et nous devons encore raisonner sur les états globaux, après avoir déballé toutes les indirectes.

Comme les gens du printemps qui déclarent somptueusement les états globaux en XML et pensent que c'est supérieur.

@Jon Skeet if I create a new instance of an object Maintenant vous avez deux choses à raisonner: l'état dans l'objet et l'état de l'environnement hébergeant l'objet.


51
2017-08-11 14:19



Il y a 2 problèmes principaux avec les variables statiques:

  • Sécurité du filetage - les ressources statiques ne sont par définition pas sécurisées
  • Code Implicity - Vous ne savez pas quand une variable statique est instanciée et si elle sera instanciée avant une autre variable statique

29
2017-08-11 13:19



Si vous utilisez le mot-clé 'static' sans le mot-clé 'final', cela devrait être un signal pour considérer attentivement votre design. Même la présence d'une «finale» n'est pas une passe libre, car un objet final statique mutable peut être tout aussi dangereux.

J'évaluerais quelque part autour de 85% du temps je vois un «statique» sans une «finale», c'est faux. Souvent, je vais trouver des solutions de contournement étranges pour masquer ou cacher ces problèmes.

Merci de ne pas créer de mutables statiques. Collections en particulier. En général, les collections doivent être initialisées lorsque leur objet contenant est initialisé et doivent être conçues de sorte qu'elles soient réinitialisées ou oubliées lorsque leur objet contenant est oublié.

L'utilisation de la statique peut créer des bugs très subtils qui causeront des jours de douleur aux ingénieurs. Je sais, parce que j'ai à la fois créé et chassé ces bugs.

Si vous souhaitez plus de détails, s'il vous plaît lire sur ...

Pourquoi ne pas utiliser les statistiques?

Il y a beaucoup de problèmes avec la statique, y compris l'écriture et l'exécution de tests, ainsi que des bugs subtils qui ne sont pas immédiatement évidents.

Le code qui repose sur des objets statiques ne peut pas être facilement testé à l'unité, et les statistiques ne peuvent pas être facilement moquées (généralement).

Si vous utilisez des statistiques, il n'est pas possible d'échanger l'implémentation de la classe afin de tester des composants de niveau supérieur. Par exemple, imaginez un CustomerDAO statique qui renvoie les objets Client qu'il charge à partir de la base de données. Maintenant, j'ai une classe CustomerFilter, qui doit accéder à certains objets Client. Si CustomerDAO est statique, je ne peux pas écrire un test pour CustomerFilter sans avoir préalablement initialisé ma base de données et rempli des informations utiles.

Et la population de base de données et l'initialisation prend beaucoup de temps. Et selon mon expérience, votre cadre d'initialisation de la base de données changera au fil du temps, ce qui signifie que les données se transformeront et que les tests risquent de se casser. IE, imaginez que le client 1 était un VIP, mais le cadre d'initialisation de la base de données a changé, et maintenant le client 1 n'est plus VIP, mais votre test a été codé en dur pour charger le client 1 ...

Une meilleure approche consiste à instancier un CustomerDAO et à le passer dans le CustomerFilter lorsqu'il est construit. (Une approche encore meilleure consisterait à utiliser Spring ou un autre cadre d'inversion de contrôle.

Une fois que vous faites cela, vous pouvez rapidement simuler ou remplacer un DAO alternatif dans votre test CustomerFilterTest, vous permettant d'avoir plus de contrôle sur le test,

Sans le DAO statique, le test sera plus rapide (pas d'initialisation db) et plus fiable (car il n'échouera pas lorsque le code d'initialisation db change). Par exemple, dans ce cas, s'assurer que le client 1 est et sera toujours un VIP, en ce qui concerne le test.

Exécuter des tests

Les statistiques provoquent un réel problème lors de l'exécution simultanée de suites de tests unitaires (par exemple, avec votre serveur d'intégration continue). Imaginez une carte statique des objets Socket réseau qui reste ouverte d'un test à l'autre. Le premier test peut ouvrir un socket sur le port 8080, mais vous avez oublié d'effacer la carte lorsque le test est détruit. Maintenant, lorsqu'un second test est lancé, il est susceptible de se bloquer quand il essaye de créer un nouveau Socket pour le port 8080, puisque le port est toujours occupé. Imaginons également que les références Socket de votre collection statique ne soient pas supprimées et (à l'exception de WeakHashMap) ne soient jamais éligibles à la collecte des ordures, provoquant ainsi une fuite de mémoire.

C'est un exemple trop généralisé, mais dans les grands systèmes, ce problème se produit tout le temps. Les gens ne pensent pas aux tests unitaires démarrant et arrêtant leur logiciel à plusieurs reprises dans la même JVM, mais c'est un bon test de la conception de votre logiciel, et si vous avez des aspirations à la haute disponibilité, c'est quelque chose dont vous devez être conscient.

Ces problèmes surviennent souvent avec des objets de structure, par exemple, les couches d'accès à la base de données, de mise en cache, de messagerie et de journalisation. Si vous utilisez Java EE ou certains des meilleurs frameworks, ils gèrent probablement beaucoup de choses pour vous, mais si comme moi vous avez affaire à un système hérité, vous pouvez avoir beaucoup de frameworks personnalisés pour accéder à ces couches.

Si la configuration du système qui s'applique à ces composants de structure change entre tests unitaires et que le framework de test unitaire ne supprime pas et ne reconstruit pas les composants, ces modifications ne peuvent pas prendre effet et lorsqu'un test repose sur ces modifications, elles échouent .

Même les composants non-framework sont soumis à ce problème. Imaginez une carte statique appelée OpenOrders. Vous écrivez un test qui crée quelques ordres ouverts, et vérifie qu'ils sont tous dans le bon état, puis le test se termine. Un autre développeur écrit un deuxième test qui met les commandes dont il a besoin dans la carte OpenOrders, puis affirme que le nombre de commandes est exact. Exécutés individuellement, ces tests réussiraient tous deux, mais s'ils sont exécutés ensemble dans une suite, ils échoueront.

Pire, l'échec peut être basé sur l'ordre dans lequel les tests ont été exécutés.

Dans ce cas, en évitant la statique, vous évitez le risque de persistance des données entre les instances de test, garantissant ainsi une meilleure fiabilité des tests.

Bugs subtils

Si vous travaillez dans un environnement à haute disponibilité ou partout où des threads peuvent être démarrés et arrêtés, la même préoccupation mentionnée ci-dessus avec les suites de tests unitaires peut s'appliquer lorsque votre code est également exécuté en production.

Lorsque vous traitez des threads, plutôt que d'utiliser un objet statique pour stocker des données, il est préférable d'utiliser un objet initialisé pendant la phase de démarrage du thread. Ainsi, chaque fois que le thread est démarré, une nouvelle instance de l'objet (avec une configuration potentiellement nouvelle) est créée et vous évitez que les données d'une instance du thread ne passent à l'instance suivante.

Lorsqu'un thread meurt, un objet statique n'est pas réinitialisé ou récupéré. Imaginez que vous avez un thread appelé "EmailCustomers", et quand il démarre, il remplit une collection de chaînes statique avec une liste d'adresses email, puis commence à envoyer par email chacune des adresses. Disons que le thread est interrompu ou annulé d'une manière ou d'une autre, votre framework de haute disponibilité redémarre le thread. Ensuite, lorsque le thread démarre, il recharge la liste des clients. Mais comme la collection est statique, elle peut conserver la liste des adresses e-mail de la collection précédente. Maintenant, certains clients peuvent obtenir des courriels en double.

Un côté: la finale statique

L'utilisation de "static final" est effectivement l'équivalent Java d'une définition C #, bien qu'il existe des différences d'implémentation technique. Un #define C / C ++ est extrait du code par le pré-processeur, avant la compilation. Une "finale statique" Java finira par la mémoire résidant sur la pile. De cette façon, il est plus proche d'une variable "static const" en C ++ que d'une variable #define.

Résumé

J'espère que cela aide à expliquer quelques raisons fondamentales pour lesquelles la statique est problématique. Si vous utilisez un framework Java moderne comme Java EE ou Spring, etc., vous ne rencontrerez peut-être pas beaucoup de ces situations, mais si vous travaillez avec un grand nombre de code hérité, ils peuvent devenir beaucoup plus fréquents.


24
2018-04-24 23:15



Puisque personne ne l'a mentionné: concurrence Les variables statiques peuvent vous surprendre si plusieurs threads lisent et écrivent dans la variable statique. Ceci est courant dans les applications Web (par exemple, ASP.NET) et peut provoquer des bogues plutôt exaspérants. Par exemple, si vous avez une variable statique qui est mise à jour par une page, et que la page est demandée par deux personnes "presque en même temps", un utilisateur peut obtenir le résultat attendu par l'autre utilisateur, ou pire.

les statiques réduisent les interdépendances sur les autres parties du code. Ils peuvent agir en tant que détenteurs de l'état parfait

J'espère que vous êtes prêt à utiliser des verrous et à gérer les conflits.

*Réellement, Pré Sangha mentionné.


13
2017-08-11 21:46



si je devais faire 10 000 appels à une fonction dans une classe, je serais   heureux de rendre la méthode statique et d'utiliser un simple   class.methodCall () au lieu d'encombrer la mémoire avec 10 000   instances de la classe, non?

Vous devez équilibrer le besoin d'encapsuler des données dans un objet avec un état, par rapport à la nécessité de simplement calculer le résultat d'une fonction sur certaines données.

De plus, les statistiques réduisent les interdépendances sur les autres parties du code.

Donc, l'encapsulation. Dans les grandes applications, les statistiques ont tendance à produire du code spaghetti et ne permettent pas facilement le refactoring ou le test.

Les autres réponses fournissent également de bonnes raisons contre l'utilisation excessive de la statique.


12
2017-08-11 13:22



Les variables statiques sont généralement considérées comme mauvaises parce qu'elles représentent l'état global et sont donc beaucoup plus difficiles à raisonner. En particulier, ils cassent les hypothèses de la programmation orientée objet. Dans la programmation orientée objet, chaque objet a son propre état, représenté par des variables d'instance (non statiques). Les variables statiques représentent l'état d'une instance à l'autre, ce qui peut être beaucoup plus difficile à tester. C'est principalement parce qu'il est plus difficile d'isoler les changements de variables statiques à un seul test.

Cela étant dit, il est important de faire une distinction entre les variables statiques régulières (généralement considérées comme mauvaises) et les variables statiques finales (constantes AKA, pas si mauvaises).


10
2017-08-11 13:24



À mon avis, il ne s'agit presque jamais de performance, mais de design. Je ne considère pas que l'utilisation de méthodes statiques soit incorrecte en ce qui concerne l'utilisation de variables statiques (mais je suppose que vous parlez en fait d'appels de méthodes).

Il s'agit simplement de savoir comment isoler la logique et lui donner une bonne place. Parfois, cela justifie l'utilisation de méthodes statiques dont java.lang.Math est un bon exemple. Je pense quand vous nommez la plupart de vos cours XxxUtil ou Xxxhelper tu ferais mieux de reconsidérer ton design.


8
2017-08-11 13:23