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


635

Je suis un programmeur Java qui est nouveau dans le monde de l'entreprise. Récemment, j'ai développé une application utilisant Groovy et Java. Tout au long du code que j'ai écrit, j'ai utilisé un bon nombre de statistiques. Le lot technique senior m'a 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 assez contre l'utilisation de variables statiques.

Je trouve les variables statiques plus pratiques à utiliser. Et je suppose qu'ils sont aussi efficaces (veuillez me corriger si je me trompe), car 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()au lieu de encombrant la mémoire avec 10 000 instances de la classe, non?

De plus, la statique réduit les interdépendances avec les autres parties du code. Ils peuvent agir comme des détenteurs d'État parfaits. Ajoutant à cela, je trouve que la statique est largement implémentée dans certains langages comme Smalltalk et Scala . Alors, pourquoi cette oppression pour 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.


43
Juste pour le plaisir de dire, il n'y a pas de variables ou de méthodes statiques sur Smalltalk ou Scala, exactement parce que les méthodes et les variables statiques sont contraires aux principes de la POO.
Maurício Linhares

87
Au moins une affirmation que vous faites est plutôt curieuse: "la statique réduit les interdépendances avec les autres parties du code". En général, ils resserrent les dépendances. Le code où l'appel est effectué est très étroitement lié au code appelé. Aucune abstraction entre, dépendance directe.
Arne Deutsch

11
Bonne question ... plus d'un Programmers.SE Questions tho?
WernerCD

26
Votre deuxième paragraphe concerne un sujet entièrement différent, à savoir les méthodes statiques .
Paul

8
La programmation fonctionnelle désapprouve également l'état global. Si jamais vous (et vous devriez ) entrer dans la PF un jour, soyez prêt à abandonner la notion d'état global.
new123456

Réponses:


689

Les variables statiques représentent l'état global. C'est difficile à raisonner et à 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 bon moment, mais le plus grand concept auquel penser est que plus la portée de quelque chose est étroite, plus il est facile de raisonner. Nous sommes bons à penser aux petites choses, mais il est difficile de raisonner sur l'état d'un système à un million de lignes s'il n'y a pas de modularité. Cela s'applique à toutes sortes de choses, d'ailleurs - pas seulement aux variables statiques.


57
Ces derniers temps, cela semble être un argument, que le code soit testable ou non. C'est un raisonnement plutôt erroné. L'argument doit être «bonne conception», et généralement une bonne conception peut être testée. Mais pas l'inverse: "Je ne peux pas le tester car il doit être de mauvaise conception." Ne vous méprenez pas, je suis d'accord avec votre message en général.
M Platvoet

144
@M Platvoet: Je dirais qu'étant donné le choix entre deux conceptions par ailleurs également valables, celle testable est supérieure. Être testable certainement ne signifie pas d'être bien conçu, mais je l' ai rarement rencontré des conceptions de bonnes invérifiables, et je pense qu'ils sont assez rares que je n'ai aucun problème à faire testabilité un but général indicateur contribuer à une bonne conception.
Jon Skeet

9
@M Platvoet - La testabilité affecte à la fois la maintenabilité et la fiabilité, et je considérerais ces facteurs majeurs dans la qualité de la conception. Ce ne sont certainement pas les seuls facteurs, mais à mon humble avis, le coût d'un code donné est une combinaison de cycles machine, de cycle développeur et de cycle utilisateur. La testabilité atteint deux de ces trois.
Justin Morgan

5
@M Platvoet - La testabilité a également tendance à affecter la réutilisabilité, car une classe découplée est généralement plus facile à réutiliser.
TrueWill

13
M Platvoet - Je ne suis pas d'accord avec votre premier commentaire ici. Je pense que si quelque chose ne peut pas être testé, alors c'est une mauvaise conception; parce que si je ne peux pas le tester, je ne peux pas savoir que cela fonctionne. Achèteriez-vous une voiture si le vendeur vous disait "La conception de ce modèle l'empêche d'être testée, donc je ne sais pas si elle fonctionne réellement"? La testabilité est si cruciale pour les logiciels (ainsi que pour les voitures), qu'une conception compétente EXIGE qu'elle soit incluse.
Dawood ibn Kareem

277

Ce n'est pas très orienté objet: une raison pour laquelle la statique peut être considérée comme "mauvaise" par certaines personnes est qu'elle est contraire au paradigme orienté objet . En particulier, il viole le principe selon lequel les données sont encapsulées dans des objets (qui peuvent être étendues, cacher des informations, etc.). La statique, dans la façon dont vous décrivez leur utilisation, consiste essentiellement à les utiliser comme variable globale pour éviter de traiter des problèmes tels que la portée. Cependant, les variables globales sont 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 attend de vous que vous écriviez du "bon code orienté objet" et que vous vouliez vraiment écrire "

Il existe de nombreux gotchyas en Java lorsque 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 représenter la valeur de la variable statique et gâcher l'état de l'autre? Ou que se passe-t-il lorsque vous étendez la classe, pouvez-vous remplacer le membre statique? Votre machine virtuelle manque de mémoire car vous avez un nombre insensé de statiques et cette mémoire ne peut pas être récupérée pour d'autres objets d'instance nécessaires?

Durée de vie des objets: en outre, les statiques ont une durée de vie qui correspond à la totalité de l'exécution du programme. Cela signifie que, même une fois que 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 demandé à votre classe d'exécuter une fonction particulière 10 000 fois, une fois ces 10 000 appels effectués , et vous supprimez vos références à l'instance unique, toutes vos variables statiques peuvent être récupérées et réutilisées.

Empêche une certaine réutilisation: De plus, les méthodes statiques ne peuvent pas être utilisées pour implémenter une interface, donc 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 meilleurs moyens de résoudre le problème de vitesse que de considérer uniquement l'avantage de l'invocation étant généralement 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 aligné, 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 du 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 du compilateur qui doivent être activés pour l'application en général. Peut-être, la conception devrait être configurée de sorte que les 10 000 exécutions puissent être multi-thread et profiter des cœurs multi-processeurs. Si la portabilité n'est pas

Si, pour une raison quelconque, vous ne voulez pas plusieurs copies d'un objet, le motif de conception singleton, présente des avantages sur les objets statiques, tels que la sécurité des threads (en supposant que votre singleton est bien codé), permettant l'initialisation paresseuse, garantissant que l'objet a été correctement initialisé lors de son utilisation, la sous-classification, les avantages de tester et de refactoriser votre code, sans oublier, si à un moment donné vous changez d'avis de ne vouloir qu'une seule instance d'un objet, il est BEAUCOUP plus facile de supprimer le code pour éviter les instances en double que de refactoriser tout votre code de variable statique pour utiliser des variables d'instance. J'ai dû le faire avant, ce n'est pas amusant, et vous finissez par devoir éditer beaucoup plus de classes, ce qui augmente votre risque d'introduire de nouveaux bugs ... tellement mieux pour mettre les choses en place "correctement" la première fois, même s'il semble avoir ses inconvénients. Pour moi, le retravail requis si vous décidez en cours de route que vous avez besoin de plusieurs copies de quelque chose est probablement l'une des raisons les plus convaincantes d'utiliser la statique aussi rarement que possible. Et donc je serais également en désaccord avec votre affirmation selon laquelle la statique réduit les interdépendances, je pense que vous vous retrouverez avec du code plus couplé si vous avez beaucoup de statistiques accessibles directement, plutôt qu'un objet qui "sait comment faire" quelque chose "sur lui-même.


11
J'aime votre réponse, je pense qu'elle se concentre sur les bons compromis à considérer autour de la statique plutôt que sur certains des harengs rouges comme la concurrence et la portée. Et +1 pour les singletons, une meilleure question aurait vraiment pu être de savoir quand utiliser des variables / méthodes statiques vs des singletons ...
studgeek

2
Même si le singleton lui-même peut être thread-safe (par exemple en utilisant des synchronizedméthodes), cela ne signifie pas que le code appelant est exempt de conditions de concurrence par rapport à l'état singleton.
André Caron

8
De plus, la statique n'est pas contraire au paradigme de la POO. Beaucoup de fanatiques de POO vous diront que la classe est un objet, et la méthode statique est une méthode de l'objet de classe, plutôt que ses instances. Ce phénomène est moins présent en Java. D'autres langages, tels que Python, vous permettent d'utiliser des classes comme variables et vous pouvez accéder à des méthodes statiques comme méthodes de cet objet.
André Caron

4
La dernière ligne du troisième paragraphe devrait lire, toutes vos variables non statiques , si je ne me trompe pas.
Steve

1
Object Lifetime, est un point très important mentionné par @jessica.
Abhidemon

93

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.

Étant donné que les statiques vivent dans un seul 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 entraîne des problèmes d'isolement de l'impact du changement et affecte ainsi la gestion des tests.

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


59

Non. Les États mondiaux 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 mondiaux sont une nécessité absolue. Nous ne pouvons pas éviter les États mondiaux. Nous ne pouvons pas éviter de raisonner sur les États mondiaux. - Si nous voulons comprendre notre sémantique d'application.

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

Comme les gens du printemps qui déclarent somptueusement les états mondiaux en xml et pensent en quelque sorte que c'est supérieur.

@ Jon Skeet if I create a new instance of an objectvous avez maintenant deux raisons de raisonner - l'état dans l'objet et l'état de l'environnement hébergeant l'objet.


10
"J'ai deux raisons de raisonner". Pas si je fais mon test uniquement en fonction de l'état de l'objet. Ce qui est plus facile, l'état le moins global que j'ai.
DJClayworth

2
L'injection de dépendances n'a rien à voir avec l'état global ou la visibilité globale, même le conteneur lui-même n'est pas global. Par rapport au code "normal", la seule chose supplémentaire qu'un objet géré par conteneur est visible est le conteneur lui-même. En fait, DI est très couramment utilisé pour éviter le modèle Singleton.
Floegipoky

31

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

  • Sécurité des threads - les ressources statiques ne sont pas par définition thread-safe
  • Implicité du code - Vous ne savez pas quand une variable statique est instanciée et si elle sera ou non instanciée avant une autre variable statique

Je pense que Jon Skeet faisait référence au même commentaire que vous avez posté.
RG-3

13
Je ne reçois pas le point sur la sécurité des threads, je pense que rien n'est sûr pour les threads à moins que vous ne le fassiez. Cela ne semble pas du tout lié à des choses statiques, veuillez me corriger si je manque quelque chose.
Zmaster

1
@Zmaster - S'il est vrai que la sécurité des threads n'est pas un problème exclusif aux variables statiques, parce que par leur définition, elles doivent être appelées à partir de et par des contextes différents, elles sont plus élaguées pour elles
élancées

2
@sternr Je comprends ce que tu veux dire, événement si "différents contextes" n'est pas nécessairement égal à "différents threads". Mais il est vrai que la sécurité des threads doit souvent être prise en compte avec les ressources statiques. Vous devriez envisager de clarifier la phrase.
Zmaster

Il existe par exemple des utilisations valides pour les threads des ressources statiques. Logger final statique privé LOG = Logger.getLogger (Foo.class); final statique statique AtomicInteger x = nouveau AtomicInteger (0); Si je comprends bien, les affectations statiques de ressources comme celle-ci sont garanties thread-safe par le chargeur de classe. L'instance de l'enregistreur est ou non thread-safe indépendamment de l'endroit où vous lui affectez le pointeur. Garder l'état en statique n'est probablement pas une bonne idée, mais il n'y a aucune raison pour qu'il ne soit pas thread-safe.
teknopaul

29

Si vous utilisez le mot clé «statique» sans le mot clé «final», cela devrait être un signal pour examiner attentivement votre conception. Même la présence d'un «final» n'est pas un laissez-passer, puisqu'un objet final statique mutable peut être tout aussi dangereux.

J'évaluerais quelque part environ 85% du temps où je vois un «statique» sans un «final», c'est FAUX. Souvent, je trouverai d'étranges solutions de contournement pour masquer ou masquer ces problèmes.

Veuillez ne pas créer de mutables statiques. Surtout les collections. En général, les collections doivent être initialisées lorsque leur objet conteneur est initialisé et doivent être conçues de manière à être réinitialisées ou oubliées lorsque leur objet conteneur est oublié.

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

Si vous souhaitez plus de détails, lisez la suite…

Pourquoi ne pas utiliser la statique?

Il existe de nombreux problèmes avec la statique, y compris l'écriture et l'exécution de tests, ainsi que des bogues 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 de la statique, 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 Customer qu'il charge à partir de la base de données. J'ai maintenant une classe CustomerFilter, qui doit accéder à certains objets Customer. Si CustomerDAO est statique, je ne peux pas écrire de test pour CustomerFilter sans d'abord initialiser ma base de données et remplir des informations utiles.

Et le remplissage et l'initialisation de la base de données prennent beaucoup de temps. Et d'après mon expérience, votre cadre d'initialisation de base de données changera au fil du temps, ce qui signifie que les données se transformeront et que les tests pourraient 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 lors de sa construction. (Une approche encore meilleure consisterait à utiliser Spring ou un autre cadre d'inversion de contrôle.

Une fois cette opération effectuée, vous pouvez rapidement simuler ou supprimer un autre DAO dans votre CustomerFilterTest, ce qui vous permet 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écution de tests

La statique pose un vrai 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 d'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é de vider la carte lorsque le test est détruit. Désormais, lorsqu'un deuxième test se lance, il risque de se bloquer lorsqu'il essaie de créer un nouveau socket pour le port 8080, car le port est toujours occupé. Imaginez également que les références de socket dans votre collection statique ne soient pas supprimées et (à l'exception de WeakHashMap) ne soient jamais éligibles pour être récupérées, provoquant une fuite de mémoire.

Il s'agit d'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 à une haute disponibilité, c'est quelque chose dont vous devez être conscient.

Ces problèmes surviennent souvent avec les objets d'infrastructure, par exemple, vos 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 cela pour vous, mais si comme moi vous avez affaire à un système hérité, vous pourriez avoir beaucoup de frameworks personnalisés pour accéder à ces couches.

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

Même les composants non-framework sont sujets à ce problème. Imaginez une carte statique appelée OpenOrders. Vous écrivez un test qui crée quelques commandes ouvertes, et vous vérifiez qu'elles sont toutes dans le bon état, puis le test se termine. Un autre développeur écrit un deuxième test qui place les commandes dont il a besoin dans la carte OpenOrders, puis affirme que le nombre de commandes est précis. Exécutés individuellement, ces tests réussiraient tous les deux, mais lorsqu'ils s'exécuteraient ensemble dans une suite, ils échoueraient.

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 persister les 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 n'importe où où les threads peuvent être démarrés et arrêtés, le même problème mentionné ci-dessus avec les suites de tests unitaires peut également s'appliquer lorsque votre code s'exécute en production.

Lorsqu'il s'agit de 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. De cette façon, 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 saignent jusqu'à l'instance suivante.

Lorsqu'un thread meurt, un objet statique n'est pas réinitialisé ni récupéré. Imaginez que vous ayez un thread appelé «EmailCustomers», et quand il démarre, il remplit une collection de chaînes statiques avec une liste d'adresses e-mail, puis commence à envoyer par e-mail chacune des adresses. Supposons que le thread soit interrompu ou annulé d'une manière ou d'une autre, de sorte que votre infrastructure à 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 recevoir des e-mails en double.

An Side: finale statique

L'utilisation de «final statique» est effectivement l'équivalent Java d'un C #define, bien qu'il existe des différences de mise en œuvre technique. AC / C ++ #define est échangé hors du code par le pré-processeur, avant la compilation. Une «finale statique» Java terminera la mémoire résidente sur la pile. De cette façon, elle est plus semblable à une variable «const statique» en C ++ qu'à une #define.

Sommaire

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 corps de code hérité, elles peuvent devenir beaucoup plus fréquentes.


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 il peut provoquer des bogues plutôt exaspérants. Par exemple, si vous avez une variable statique 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.

la statique réduit les interdépendances avec les autres parties du code. Ils peuvent agir comme des détenteurs d'État parfaits

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

* En fait, Preet Sangha l'a mentionné.


5
Les variables d'instance n'ont aucun avantage de sécurité des threads par rapport à la statique, ce sont toutes des variables non protégées. Au lieu de cela, tout se résume à la façon dont vous protégez le code qui accède à ces variables.
studgeek

2
Je n'ai pas vraiment fait cette affirmation, mais pour les besoins de la discussion: la séparation est une forme de protection. Les états de thread sont séparés; l'état mondial ne l' est pas . Une variable d'instance n'a pas besoin de protection à moins qu'elle ne soit explicitement partagée entre les threads; une variable statique est toujours partagée par tous les threads du processus.
Justin M. Keyes

Je souhaite que les variables thread-statiques soient davantage un concept de première classe, car elles peuvent être très utiles pour rendre les informations disponibles en toute sécurité à un appel de sous-programme encapsulé sans avoir à passer ces informations à travers chaque couche d'habillage. Par exemple, si un objet avait des méthodes pour le rendre dans le contexte graphique actuel du thread, et qu'il existait des méthodes pour enregistrer / restaurer le contexte graphique actuel, les utiliser pouvait souvent être plus propre que de devoir passer le contexte graphique à chaque appel de méthode.
supercat

15

Résumant quelques avantages et inconvénients de base de l'utilisation de méthodes statiques en Java:

Avantages:

  1. Accessible globalement, c'est-à-dire non lié à une instance d'objet particulière.
  2. Une instance par JVM.
  3. Accessible en utilisant le nom de classe (aucun objet requis).
  4. Contient une valeur unique applicable à toutes les instances.
  5. Chargez au démarrage de la JVM et meurt lorsque la JVM s'arrête.
  6. Ils ne modifient pas l'état de l'objet.

Désavantages:

  1. Les membres statiques font toujours partie de la mémoire, qu'ils soient utilisés ou non.
  2. Vous ne pouvez pas contrôler la création et la destruction de variable statique. Utilement, ils ont été créés lors du chargement du programme et détruits lors du déchargement du programme (ou lorsque la JVM s'arrête).
  3. Vous pouvez sécuriser les threads statiques en utilisant la synchronisation, mais vous avez besoin d'efforts supplémentaires.
  4. Si un thread change la valeur d'une variable statique qui peut éventuellement casser la fonctionnalité d'autres threads.
  5. Vous devez savoir «statique» avant de l'utiliser.
  6. Vous ne pouvez pas remplacer les méthodes statiques.
  7. La sérialisation ne fonctionne pas bien avec eux.
  8. Ils ne participent pas au polymorphisme d'exécution.
  9. Il y a un problème de mémoire (dans une certaine mesure mais pas beaucoup je suppose) si un grand nombre de variables / méthodes statiques sont utilisées. Parce qu'ils ne seront pas récupérés à la fin du programme.
  10. Les méthodes statiques sont également difficiles à tester.

Les inconvénients 6, 7, 8 et 10 sont les inconvénients des langages / cadres utilisés, et non les inconvénients des variables statiques en général. Les inconvénients 1, 4 et 5 existent également pour d'autres solutions, comme un modèle singleton fourni par un cadre. (Je n'ai pas voté pour la réponse, car je suis d'accord sur le reste et c'est une belle collection.)
peterh - Réintègre Monica

13

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 class.methodCall () simple dessus au lieu d'encombrer la mémoire avec 10 000 instances de la classe, n'est-ce pas?

Vous devez équilibrer la nécessité 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, la statique réduit les interdépendances avec les autres parties du code.

L'encapsulation aussi. Dans les grandes applications, la statique a tendance à produire du code spaghetti et ne permet pas facilement la refactorisation ou le test.

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


13

Les variables statiques sont généralement considérées comme mauvaises car elles représentent un état global et sont donc beaucoup plus difficiles à raisonner. En particulier, ils brisent 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 entre les instances, ce qui peut être beaucoup plus difficile à tester unitaire. Cela est principalement dû au fait qu'il est plus difficile d'isoler les modifications apportées aux 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 mal).


4
"Les variables statiques représentent l'état entre les classes" ... Je pense que vous voulez dire "les variables statiques représentent l'état entre les instances"? +1 pour "les constantes finales statiques AKA, pas si mal". Puisque la valeur ne peut pas changer, tout ce qui en dépend à un moment donné ne peut pas implicitement changer son comportement ultérieurement - la valeur est la même.
Jared Updike

«Les variables statiques représentent l'état entre les instances» est une bien meilleure façon de l'indiquer. J'ai édité ma réponse.
Jack Edmonds

9

À mon avis, il ne s'agit presque jamais de performances, c'est de design. Je ne considère pas l'utilisation de méthodes statiques comme incorrecte par rapport à l'utilisation de variables statiques (mais je suppose que vous parlez en fait d'appels de méthode).

Il s'agit simplement d'isoler la logique et de lui donner une bonne place. Parfois, cela justifie l'utilisation de méthodes statiques, ce qui java.lang.Mathest un bon exemple. Je pense que lorsque vous nommez la plupart de vos classes XxxUtilou que Xxxhelpervous feriez mieux de reconsidérer votre conception.


3
Les méthodes statiques sans effet secondaire pur sont parfaitement fines IMO. Mais un état global mutable l'est rarement et j'interprète le PO comme un état global.
CodesInChaos

1
@CodeInChaos est totalement d'accord. Je trouve que l'OP n'est pas entièrement clair sur la différence entre les méthodes statiques et les vars.
M Platvoet

8

Je viens de résumer certains des points soulevés dans les réponses. Si vous trouvez quelque chose de mal, n'hésitez pas à le corriger.

Mise à l'échelle: nous avons exactement une instance d'une variable statique par machine virtuelle Java. Supposons que nous développons un système de gestion de bibliothèque et nous avons décidé de mettre le nom du livre une variable statique car il n'y en a qu'une par livre. Mais si le système se développe et que nous utilisons plusieurs machines virtuelles Java, nous n'avons pas de moyen de savoir à quel livre nous avons affaire?

Thread-Safety: les variables d'instance et les variables statiques doivent être contrôlées lorsqu'elles sont utilisées dans un environnement multithread. Mais dans le cas d'une variable d'instance, elle n'a pas besoin de protection à moins qu'elle ne soit explicitement partagée entre les threads mais dans le cas d'une variable statique, elle est toujours partagée par tous les threads du processus.

Test: Bien qu'un design testable ne soit pas égal à un bon design, nous observerons rarement un bon design qui n'est pas testable. Comme les variables statiques représentent l'état global et il devient très difficile de les tester.

Raisonnement sur l'état: si je crée une nouvelle instance d'une classe, nous pouvons raisonner sur l'état de cette instance, mais si elle a des variables statiques, elle pourrait être dans n'importe quel état. Pourquoi? Parce qu'il est possible que la variable statique ait été modifiée par une instance différente car la variable statique est partagée entre les instances.

Sérialisation: La sérialisation ne fonctionne pas non plus bien avec eux.

Création et destruction: La création et la destruction de variables statiques ne peuvent pas être contrôlées. Habituellement, ils sont créés et détruits au moment du chargement et du déchargement du programme. Cela signifie qu'ils sont mauvais pour la gestion de la mémoire et ajoutent également le temps d'initialisation au démarrage.

Et si on en avait vraiment besoin?

Mais parfois, nous pouvons en avoir un réel besoin. Si nous ressentons vraiment le besoin de nombreuses variables statiques qui sont partagées à travers l'application, alors une option est d'utiliser le modèle de conception Singleton qui aura toutes ces variables. Ou nous pouvons créer un objet qui aura ces variables statiques et peut être transmis.

De plus, si la variable statique est marquée finale, elle devient une constante et la valeur qui lui est attribuée une fois ne peut pas être modifiée. Cela signifie qu'il nous sauvera de tous les problèmes auxquels nous sommes confrontés en raison de sa mutabilité.


7

Il me semble que vous posez des questions sur les variables statiques, mais vous indiquez également des méthodes statiques dans vos exemples.

Les variables statiques ne sont pas mauvaises - elles ont leur adoption en tant que variables globales comme des constantes combinées dans la plupart des cas avec le modificateur final, mais comme il l'a dit, ne les abusez pas.

Méthodes statiques ou méthode utilitaire. Ce n'est généralement pas une mauvaise pratique de les utiliser, mais la principale préoccupation est qu'elles pourraient entraver les tests.

Comme exemple d'un grand projet java qui utilise beaucoup de statistiques et le fait correctement, regardez Play! cadre . Il y a aussi une discussion à ce sujet dans SO.

Les variables / méthodes statiques combinées à l'importation statique sont également largement utilisées dans les bibliothèques qui facilitent la programmation déclarative en java comme: le rendre facile ou Hamcrest . Ce ne serait pas possible sans beaucoup de variables et de méthodes statiques.

Les variables statiques (et les méthodes) sont donc bonnes, mais utilisez-les judicieusement!


6

Les variables statiques créent surtout un problème de sécurité des données (à tout moment modifié, tout le monde peut changer, accès direct sans objet, etc.)

Pour plus d'informations, lisez ces remerciements.


6

On pourrait suggérer que dans la plupart des cas où vous utilisez une variable statique, vous voulez vraiment utiliser le modèle singleton .

Le problème avec les états globaux est que, parfois, ce qui a du sens comme global dans un contexte plus simple, doit être un peu plus flexible dans un contexte pratique, et c'est là que le modèle singleton devient utile.


5

Encore une autre raison: la fragilité.

Si vous avez une classe, la plupart des gens s'attendent à pouvoir la créer et l'utiliser à volonté.

Vous pouvez documenter ce n'est pas le cas, ou vous protéger contre cela (modèle singleton / usine) - mais c'est un travail supplémentaire, et donc un coût supplémentaire. Même alors, dans une grande entreprise, il y a des chances que quelqu'un essaie à un moment donné d'utiliser votre classe sans prêter pleinement attention à tous les bons commentaires ou à l'usine.

Si vous utilisez beaucoup de variables statiques, cela se cassera. Les bugs sont chers.

Entre une amélioration des performances de .0001% et une robustesse au changement par des développeurs potentiellement désemparés, dans la plupart des cas, la robustesse est le bon choix.


4

Je trouve les variables statiques plus pratiques à utiliser. Et je suppose qu'ils sont également efficaces (veuillez me corriger si je me trompe) parce que si je devais faire 10000 appels à une fonction dans une classe, je serais heureux de rendre la méthode statique et d'utiliser une classe simple.methodCall () dessus au lieu d'encombrer la mémoire avec 10 000 instances de la classe, non?

Je vois ce que vous pensez, mais un simple motif Singleton fera de même sans avoir à instancier 10 000 objets.

Des méthodes statiques peuvent être utilisées, mais uniquement pour les fonctions liées au domaine d'objet et qui n'ont pas besoin ou n'utilisent pas les propriétés internes de l'objet.

ex:

public class WaterContainer {
    private int size;
    private int brand;
    ...etc

    public static int convertToGallon(int liters)...

    public static int convertToLiters(int gallon)...

}

Un singleton classique (c'est-à-dire accessible par Class.Instance) est à peine meilleur qu'une variable statique. C'est un peu plus testable, mais toujours bien pire que les conceptions où vous créez simplement une seule instance au lieu de construire votre code en supposant qu'il n'y en a qu'une.
CodesInChaos

Je ne suis pas sûr de comprendre votre commentaire! Je répondais au PO au sujet de ce qu'il a déclaré en italique à propos de l'instanciation de 10 000 objets. Je ne comprends pas pourquoi vous comparez un singleton et une variable statique? Ce que je comprends de ce que vous avez écrit, c'est que Singleton est une mauvaise conception ...! Je suppose que je vous comprends mal, car le Spring Framework fait par défaut tous les beans Singleton ;-)
Cygnusx1

Un singleton classique (qui a Class.Instance) qui porte un état mutable est une mauvaise conception de l'OMI. Dans ce cas, je préfère fortement une conception où j'obtiens les singletons que j'ai besoin d'utiliser passés en paramètre dans la classe qui les utilise (généralement avec l'aide de DI). Les singletons classiques logiquement immuables sont très bien OMI.
CodesInChaos

@ Cygnusx1 Dans le cas où il n'était pas clair pourquoi un singleton de classe (un singleton où la classe assure qu'il s'agit d'une seule copie) n'était pas facilement testable, il couple étroitement l'existence de la classe au cycle de vie du programme. Pour le tester, vous devez respecter le lancement et l'arrêt du programme, ce qui a souvent des effets secondaires sans importance pour tester la classe. S'il s'agissait effectivement de singleton (une copie dans le programme, mais pas dans le cas contraire), vous pourriez créer plusieurs copies au moment du test sans le programme, en vérifiant que le comportement dans la classe est tel qu'il devrait être pour chaque scénario de test.
Edwin Buck

4

La question de «la statique étant mauvaise» est davantage une question d'état mondial. Le moment approprié pour qu'une variable soit statique, c'est si elle n'a jamais plus d'un état; Les outils IE qui devraient être accessibles par l'ensemble du framework et toujours renvoyer les mêmes résultats pour les mêmes appels de méthode ne sont jamais «mauvais» que les statiques. Quant à votre commentaire:

Je trouve les variables statiques plus pratiques à utiliser. Et je suppose qu'ils sont aussi efficaces

La statique est le choix idéal et efficace pour les variables / classes qui ne changent jamais .

Le problème avec l'état global est l'incohérence inhérente qu'il peut créer. La documentation sur les tests unitaires résout souvent ce problème, car chaque fois qu'il existe un état global auquel peuvent accéder plus de plusieurs objets non liés, vos tests unitaires seront incomplets et ne seront pas `` unitaires ''. Comme mentionné dans cet article sur l'état global et les singletons , si les objets A et B ne sont pas liés (comme dans l'un, il n'est pas expressément fait référence à un autre), A ne devrait pas être en mesure d'affecter l'état de B.

Il existe quelques exceptions à l'interdiction de l'état global dans un bon code, comme l'horloge. Le temps est global et, dans un certain sens, il modifie l'état des objets sans avoir de relation codée.


"Le temps est global" - il existe d'autres façons de modéliser le temps dans les systèmes informatiques que de le faire implicitement et globalement changer. cf. cette enquête: "Modélisation du temps dans l'informatique: une taxonomie et une étude comparative" @ arxiv.org/abs/0807.4132
Jared Updike

4

Mon montant de 0,02 $ est que plusieurs de ces réponses confondent le problème, plutôt que de dire "les statiques sont mauvaises". Je pense qu'il vaut mieux parler de la portée et des instances.

Ce que je dirais, c'est qu'une variable statique est une variable de "classe" - elle représente une valeur qui est partagée entre toutes les instances de cette classe. En règle générale, il doit également être défini de cette manière (protégé ou privé pour la classe et ses instances).

Si vous prévoyez de mettre le comportement au niveau de la classe autour de lui et de l'exposer à un autre code, un singleton peut être une meilleure solution pour prendre en charge les modifications à l'avenir (comme l'a suggéré @Jessica). En effet, vous pouvez utiliser des interfaces au niveau instance / singleton d'une manière que vous ne pouvez pas utiliser au niveau classe - en particulier l'héritage.

Quelques réflexions sur la raison pour laquelle je pense que certains aspects des autres réponses ne sont pas au cœur de la question ...

La statique n'est pas "globale". En Java, la portée est contrôlée séparément de statique / instance.

La concurrence n'est pas moins dangereuse pour la statique que les méthodes d'instance. C'est toujours un état qui doit être protégé. Bien sûr, vous pouvez avoir 1000 instances avec une variable d'instance chacune et une seule variable statique, mais si le code accédant à l'une ou l'autre n'est pas écrit de manière sécurisée pour les threads, vous êtes toujours vissé - cela peut prendre un peu plus de temps pour que vous le réalisiez .

La gestion du cycle de vie est un argument intéressant, mais je pense qu'il est moins important. Je ne vois pas pourquoi il est plus difficile de gérer une paire de méthodes de classe comme init () / clear () que la création et la destruction d'une instance singleton. En fait, certains pourraient dire qu'un singleton est un peu plus compliqué en raison de GC.

PS, En termes de Smalltalk, beaucoup de ses dialectes ont des variables de classe, mais dans les classes Smalltalk sont en fait des instances de Metaclass donc ce sont vraiment des variables sur l'instance de Metaclass. Pourtant, j'appliquerais la même règle d'or. S'ils sont utilisés pour un état partagé entre les instances, alors ok. S'ils prennent en charge la fonctionnalité publique, vous devriez regarder un Singleton. Soupir, je manque certainement Smalltalk ....


4

Il y a deux questions principales dans votre message.

Tout d'abord, sur les variables statiques. Les variables statiques sont complètement inutiles et leur utilisation peut être évitée facilement. Dans les langages OOP en général, et en Java en particulier, les paramètres de fonction sont passés par référence, c'est-à-dire que si vous passez un objet à un funciont, vous passez un pointeur vers l'objet, vous n'avez donc pas besoin de définir de variables statiques puisque vous pouvez passer un pointeur sur l'objet vers n'importe quelle étendue qui a besoin de ces informations. Même si cela implique que vous remplissez votre mémoire de pointeurs, cela ne représentera pas nécessairement une mauvaise performance car les systèmes de pagination de la mémoire réels sont optimisés pour gérer cela, et ils conserveront en mémoire les pages référencées par les pointeurs que vous avez transmis au nouveau portée; l'utilisation de variables statiques peut amener le système à charger la page mémoire où elles sont stockées lorsqu'elles doivent être consultées (cela se produira si la page n'a pas été accédée depuis longtemps). Une bonne pratique consiste à rassembler tous ces éléments statiques dans de petites "classes de configuration", ce qui garantira que le système les mettra tous dans la même page de mémoire.

Deuxièmement, sur les méthodes statiques. Les méthodes statiques ne sont pas si mauvaises, mais elles peuvent rapidement réduire les performances. Par exemple, pensez à une méthode qui compare deux objets d'une classe et renvoie une valeur indiquant lequel des objets est le plus grand (méthode de comparaison typique), cette méthode peut être statique ou non, mais en l'invoquant, la forme non statique sera plus efficace car il ne devra résoudre que deux références (une pour chaque objet) face aux trois références qui devront résoudre la version statique de la même méthode (une pour la classe plus deux, une pour chaque objet). Mais comme je l'ai dit, ce n'est pas si mal, si nous jetons un coup d'œil à la classe Math, nous pouvons trouver beaucoup de fonctions mathématiques définies comme des méthodes statiques. C'est vraiment plus efficace que de mettre toutes ces méthodes dans la classe définissant les nombres,

En conclusion: évitez d'utiliser des variables statiques et trouvez le bon équilibre de performance lorsque vous traitez avec des méthodes statiques ou non statiques.

PS: Désolé pour mon anglais.


4

Il n'y a rien de mal avec les variables statiques en soi. C'est juste la syntaxe Java qui est cassée. Chaque classe Java définit en fait deux structures: un objet singleton qui encapsule des variables statiques et une instance. Définir les deux dans le même bloc source est un mal absolu et entraîne un code difficile à lire. Scala l'a bien fait.


3

a) Raison des programmes.

Si vous avez un programme de petite à moyenne taille, où la variable statique Global.foo est accessible, l'appel à celle-ci vient normalement de nulle part - il n'y a pas de chemin, et donc pas de chronologie, comment la variable arrive à l'endroit, où elle est utilisé. Maintenant, comment savoir qui l'a réglé à sa valeur réelle? Comment savoir ce qui se passe si je le modifie maintenant? J'ai grep sur toute la source, pour collecter tous les accès, pour savoir ce qui se passe.

Si vous savez comment vous l'utilisez, car vous venez d'écrire le code, le problème est invisible, mais si vous essayez de comprendre le code étranger, vous comprendrez.

b) En avez-vous vraiment besoin?

Les variables statiques empêchent souvent plusieurs programmes du même type de s'exécuter dans la même machine virtuelle Java avec des valeurs différentes. Souvent, vous ne prévoyez pas les utilisations, où plus d'une instance de votre programme est utile, mais si elle évolue, ou si elle est utile pour d'autres, ils pourraient rencontrer des situations, où ils aimeraient démarrer plus d'une instance de votre programme .

Seul un code plus ou moins inutile qui ne sera pas utilisé par beaucoup de gens sur une longue période de manière intensive pourrait bien aller avec des variables statiques.


3

tout (peut :) avoir son but, si vous avez un tas de threads qui doivent partager / mettre en cache des données et aussi toute la mémoire accessible (donc vous ne devez pas vous diviser en contextes au sein d'une même machine virtuelle Java), la statique est le meilleur choix

-> bien sûr, vous pouvez forcer juste un exemple, mais pourquoi?
je trouve certains des commentaires dans ce fil mal, pas la statique;)


3

Les variables statiques ne sont ni bonnes ni mauvaises. Ils représentent des attributs qui décrivent la classe entière et non une instance particulière. Si vous avez besoin d'un compteur pour toutes les instances d'une certaine classe, une variable statique serait le bon endroit pour contenir la valeur.

Des problèmes apparaissent lorsque vous essayez d'utiliser des variables statiques pour conserver des valeurs liées à l'instance.


2

Toutes les réponses ci-dessus montrent pourquoi la statique est mauvaise. La raison pour laquelle ils sont mauvais, c'est parce que cela donne la fausse impression que vous écrivez du code orienté objet, alors qu'en fait vous ne l'êtes pas. C'est tout simplement mauvais.


1
Mais est-ce que le fait de considérer votre code de manière rigide comme un paradigme standard arbitraire améliore réellement le code, ou nous plaignons-nous pour éviter de simplement écrire du code qui fonctionne?
Zoey

Oui, cela le rend meilleur, car il le rend plus facile à gérer à l'avenir, plus facile à comprendre et plus explicite.
blockhead

1
Pourquoi est-il mauvais de ne pas écrire de code OO? et pourquoi Bjarne Stroustrup n'est pas d'accord avec vous? pour n'en nommer qu'un ...
Marquis de Lorne

2
Je n'ai pas dit que c'était mal de ne pas écrire de code OO. J'ai dit que c'était mal de penser que vous écrivez du code OO, alors que tout ce que vous êtes est de déguiser des globaux derrière des méthodes et des propriétés statiques. Veuillez relire ce que j'ai écrit.
blockhead

2

Il y a beaucoup de bonnes réponses ici, en y ajoutant,

Mémoire: Les variables statiques sont actives aussi longtemps que le chargeur de classe vit [en général jusqu'à ce que la VM meure], mais ce n'est que dans le cas d'objets en vrac / références stockés en tant que statiques.

Modularisation: considérez des concepts comme IOC, dependencyInjection, proxy, etc. Tous sont complètement contre les implémentations à couplage étroit / statique.

Autres inconvénients: sécurité des filetages, testabilité


0

Pensez que si vous avez une application avec de nombreux utilisateurs et que vous avez défini un formulaire statique, chaque utilisateur modifiera également tous les autres formulaires des autres utilisateurs.


0

Je pense que les utilisations excessives de variables globales avec un mot-clé statique entraîneront également une fuite de mémoire à un moment donné de l'applica


0

De mon point de vue, la staticvariable ne devrait être que des données en lecture seule ou des variables créées par convention .

Par exemple, nous avons une interface utilisateur d'un projet, et nous avons une liste de pays, de langues, de rôles d'utilisateurs, etc. Et nous avons une classe pour organiser ces données. nous sommes absolument sûrs que l'application ne fonctionnera pas sans ces listes. donc la première chose que nous faisons sur l'application init est de vérifier cette liste pour les mises à jour et d'obtenir cette liste depuis l'api (si nécessaire). Nous convenons donc que ces données sont "toujours" présentes dans l'application. Il s'agit pratiquement de données en lecture seule, nous n'avons donc pas besoin de prendre soin de son état - en pensant à ce cas, nous ne voulons vraiment pas avoir beaucoup d'instances de ces données - ce cas semble être un candidat parfait pour être statique .


0

J'ai beaucoup joué avec la statique et puis-je vous donner une réponse légèrement différente - ou peut-être une façon légèrement différente de voir les choses?

Quand j'ai utilisé la statique dans une classe (membres et méthodes tous les deux), j'ai finalement commencé à remarquer que ma classe est en fait deux classes partageant la responsabilité - il y a la partie "statique" qui agit un peu comme un singleton et il y a le non -partie statique (une classe normale). Pour autant que je sache, vous pouvez toujours séparer complètement ces deux classes en sélectionnant simplement toutes les statistiques pour une classe et les non-statiques pour l'autre.

Cela arrivait souvent lorsque j'avais une collection statique à l'intérieur d'une classe contenant des instances de la classe et quelques méthodes statiques pour gérer la collection. Une fois que vous y pensez, il est évident que votre classe ne fait pas "juste une chose", c'est être une collection et faire quelque chose de complètement différent.

Maintenant, remanions un peu le problème: si vous divisez votre classe en une classe où tout est statique et une autre qui n'est qu'une "classe normale" et oubliez la "classe normale", votre question devient alors une classe purement statique vs Singleton qui est abordé en détail ici (et probablement une douzaine d'autres questions).

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.