web-dev-qa-db-fra.com

Pourquoi Global State est-il si mal?

Avant de commencer, permettez-moi de dire que je connais bien les concepts d'abstraction et d'injection de dépendance. Je n'ai pas besoin que mes yeux soient ouverts ici.

Eh bien, la plupart d'entre nous disent (trop) souvent sans vraiment comprendre: "N'utilisez pas de variables globales" ou "Les singletons sont mauvais parce qu'ils sont globaux". Mais qu'est-ce vraiment si mauvais à propos de l'état global inquiétant?

Disons que j'ai besoin d'une configuration globale pour mon application, par exemple des chemins de dossier système ou des informations d'identification de base de données à l'échelle de l'application.

Dans ce cas, je ne vois aucune bonne solution autre que de fournir ces paramètres dans une sorte d'espace global, qui sera généralement disponible pour l'ensemble de l'application.

Je sais que c'est mauvais d'en en abuser , mais est-ce vraiment l'espace global [~ # ~] que [~ # ~] mal? Et si c'est le cas, quelles sont les bonnes alternatives?

353
Madara's Ghost

Très brièvement, cela rend l'état du programme imprévisible.

Pour élaborer, imaginez que vous avez deux objets qui utilisent tous les deux la même variable globale. En supposant que vous n'utilisez pas de source de hasard n'importe où dans l'un ou l'autre module, la sortie d'une méthode particulière peut être prédite (et donc testée) si l'état du système est connu avant d'exécuter la méthode.

Cependant, si une méthode dans l'un des objets déclenche un effet secondaire qui modifie la valeur de l'état global partagé, vous ne savez plus quel est l'état de départ lorsque vous exécutez une méthode dans l'autre objet. Vous ne pouvez désormais plus prédire quelle sortie vous obtiendrez lorsque vous exécuterez la méthode, et vous ne pourrez donc pas la tester.

Au niveau académique, cela peut ne pas sembler si grave, mais être capable de tester le code unitaire est une étape majeure dans le processus de preuve de son exactitude (ou du moins de son adéquation à l'objectif).

Dans le monde réel, cela peut avoir des conséquences très graves. Supposons que vous ayez une classe qui remplit une structure de données globale et une classe différente qui consomme les données dans cette structure de données, modifiant son état ou les détruisant dans le processus. Si la classe de processeur exécute une méthode avant la fin de la classe de populateur, le résultat est que la classe de processeur aura probablement des données incomplètes à traiter, et la structure de données sur laquelle la classe de populateur travaillait pourrait être corrompue ou détruite. Le comportement du programme dans ces circonstances devient complètement imprévisible et entraînera probablement une perte épique.

De plus, l'état global nuit à la lisibilité de votre code. Si votre code a une dépendance externe qui n'est pas explicitement introduite dans le code, quiconque obtient le travail de maintenance de votre code devra aller le chercher pour comprendre d'où il vient.

Quant aux alternatives existantes, il est impossible de n'avoir aucun état global, mais dans la pratique, il est généralement possible de restreindre l'état global à un seul objet qui enveloppe tous les autres, et qui ne doit jamais être référencé en s'appuyant sur les règles de portée de la langue que vous utilisez. Si un objet particulier a besoin d'un état particulier, il doit le demander explicitement en le faisant passer comme argument à son constructeur ou par une méthode setter. C'est ce qu'on appelle l'injection de dépendance.

Il peut sembler idiot de passer dans un état auquel vous pouvez déjà accéder en raison des règles de portée de la langue que vous utilisez, mais les avantages sont énormes. Maintenant, si quelqu'un regarde le code isolément, il est clair de quel état il a besoin et d'où il vient. Il présente également d'énormes avantages en ce qui concerne la flexibilité de votre module de code et donc les possibilités de le réutiliser dans différents contextes. Si l'état est transmis et que les modifications apportées à l'état sont locales au bloc de code, vous pouvez passer dans n'importe quel état de votre choix (s'il s'agit du type de données correct) et demander à votre code de le traiter. Le code écrit dans ce style a tendance à avoir l'apparence d'une collection de composants peu associés qui peuvent être facilement échangés. Le code d'un module ne devrait pas se soucier d'où vient l'état, juste comment le traiter. Si vous passez l'état dans un bloc de code, ce bloc de code peut exister de manière isolée, ce n'est pas le cas si vous comptez sur l'état global.

Il existe de nombreuses autres raisons pour lesquelles le passage d'un État à un autre est largement supérieur à un État mondial. Cette réponse n'est nullement complète. Vous pourriez probablement écrire un livre entier sur les raisons pour lesquelles l'état mondial est mauvais.

293
GordonM

L'état mondial mutable est mauvais pour de nombreuses raisons:

  • Bogues de l'état global mutable - beaucoup de bogues délicats sont causés par la mutabilité. Les bogues qui peuvent être causés par une mutation de n'importe où dans le programme sont encore plus délicats, car il est souvent difficile de trouver la cause exacte
  • Mauvaise testabilité - si vous avez un état global mutable, vous devrez le configurer pour tous les tests que vous écrivez. Cela rend les tests plus difficiles (et les gens étant des gens sont donc moins susceptibles de le faire!). par exemple. dans le cas des informations d'identification de base de données à l'échelle de l'application, que se passe-t-il si un test doit accéder à une base de données de test spécifique différente de tout le reste?
  • Inflexibilité - que se passe-t-il si une partie du code nécessite une valeur à l'état global, mais une autre partie nécessite une autre valeur (par exemple, une valeur temporaire lors d'une transaction)? Vous avez soudainement un méchant refactoring sur vos mains
  • Fonction impureté - Les fonctions "pures" (c'est-à-dire celles dont le résultat ne dépend que des paramètres d'entrée et n'ont pas d'effets secondaires) sont beaucoup plus faciles à raisonner et à composer pour construire des programmes plus grands. Les fonctions qui lisent ou manipulent un état global mutable sont intrinsèquement impures.
  • compréhension du code - le comportement du code qui dépend de beaucoup de variables globales mutables est beaucoup plus difficile à comprendre - vous devez comprendre la gamme des interactions possibles avec la variable globale avant de pouvoir raisonner sur le comportement du code . Dans certaines situations, ce problème peut devenir insoluble.
  • problèmes de concurrence - l'état global mutable nécessite généralement une certaine forme de verrouillage lorsqu'il est utilisé dans une situation simultanée. C'est très difficile à obtenir correctement (est une cause de bugs) et ajoute considérablement plus de complexité à votre code (difficile/coûteux à maintenir).
  • Performance - plusieurs threads continuellement dénigrant le même état global provoquent des conflits de cache et ralentiront globalement votre système.

Alternatives à un état global mutable:

  • Paramètres de fonction - souvent négligé, mais mieux paramétrer vos fonctions est souvent le meilleur moyen d'éviter l'état global. Cela vous oblige à résoudre l'importante question conceptuelle: de quelles informations cette fonction a-t-elle besoin pour faire son travail? Parfois, il est logique d'avoir une structure de données appelée "Context" qui peut être transmise à une chaîne de fonctions qui englobe toutes les informations pertinentes.
  • Injection de dépendances - comme pour les paramètres de fonction, juste fait un peu plus tôt (lors de la construction de l'objet plutôt que de l'invocation de la fonction). Soyez prudent si vos dépendances sont des objets mutables, cela peut rapidement causer les mêmes problèmes que l'état global mutable .....
  • état global immuable est principalement inoffensif - c'est effectivement une constante. Mais assurez-vous que c'est vraiment une constante, et que vous n'allez pas être tenté de la transformer en état global mutable à un stade ultérieur!
  • singletons immuables - à peu près la même chose que l'état global immuable, sauf que vous pouvez différer l'instanciation jusqu'à ce qu'ils soient nécessaires. Utile par exemple grandes structures de données fixes qui nécessitent un pré-calcul unique et coûteux. Les singletons mutables sont bien sûr équivalents à un état global mutable et sont donc mauvais :-)
  • Dynamic binding - uniquement disponible dans certains langages comme Common LISP/Clojure, mais cela vous permet effectivement de lier une valeur dans une portée contrôlée (généralement sur une base locale de thread) qui n'affecte pas les autres threads. Dans une certaine mesure, il s'agit d'un moyen "sûr" d'obtenir le même effet qu'une variable globale, car vous savez que seul le thread d'exécution en cours sera affecté. Ceci est particulièrement utile dans le cas où vous avez plusieurs threads chacun gérant des transactions indépendantes, par exemple.
140
mikera
  1. Étant donné que votre fichue application peut l'utiliser, il est toujours incroyablement difficile de les réintégrer. Si vous changez quelque chose à voir avec votre global, tout votre code doit être changé. C'est un casse-tête de maintenance - bien plus que simplement être capable de grep pour le nom du type pour savoir quelles fonctions l'utilisent.
  2. Ils sont mauvais car ils introduisent des dépendances cachées, qui cassent le multithreading, ce qui est de plus en plus vital pour de plus en plus d'applications.
  3. L'état de la variable globale est toujours complètement peu fiable, car tout votre code pourrait y faire quoi que ce soit.
  4. Ils sont vraiment difficiles à tester.
  5. Ils rendent difficile l'appel de l'API. "Vous devez vous rappeler d'appeler SET_MAGIC_VARIABLE () avant d'appeler l'API" est juste mendiant pour quelqu'un oublie de l'appeler. Cela rend l'utilisation de l'API sujette aux erreurs, provoquant des bogues difficiles à trouver. En l'utilisant comme paramètre normal, vous forcez l'appelant à fournir correctement une valeur.

Passez simplement une référence dans les fonctions qui en ont besoin. Ce n'est pas si dur.

62
DeadMG

Si vous dites "état", cela signifie généralement "état mutable". Et l'état mutable global est totalement mauvais, car cela signifie que n'importe quelle partie du programme peut influencer n'importe quelle autre partie (en changeant l'état global).

Imaginez le débogage d'un programme inconnu: vous trouvez que la fonction A se comporte d'une certaine manière pour certains paramètres d'entrée, mais parfois elle fonctionne différemment pour les mêmes paramètres. Vous constatez qu'il utilise la variable globale x.

Vous recherchez des endroits qui modifient x, et constatez qu'il y a cinq endroits qui le modifient. Maintenant, bonne chance pour savoir dans quels cas la fonction A fait quoi ...

34
sleske

Vous avez en quelque sorte répondu à votre propre question. Ils sont difficiles à gérer lorsqu'ils sont "maltraités", mais peuvent être utiles et [quelque peu] prévisibles lorsqu'ils sont utilisés correctement, par quelqu'un qui sait comment les contenir. La maintenance et les modifications de/sur les globales sont généralement un cauchemar, aggravées par l'augmentation de la taille de l'application.

Les programmeurs expérimentés qui peuvent faire la différence entre les globaux étant la seule option et eux étant la solution facile, peut ont un minimum de problèmes à les utiliser. Mais les problèmes possibles sans fin qui peuvent survenir avec leur utilisation nécessitent des conseils contre leur utilisation.

edit: Pour clarifier ce que je veux dire, les globaux sont imprévisibles par nature. Comme pour tout ce qui est imprévisible, vous pouvez prendre des mesures pour contenir l'imprévisibilité, mais il y a toujours des limites à ce qui peut être fait. Ajoutez à cela les tracas des nouveaux développeurs rejoignant le projet devant faire face à des variables relativement inconnues, les recommandations contre l'utilisation de globaux devraient être compréhensibles.

9
orourkek

Il y a beaucoup de problèmes avec les Singletons - voici les deux plus gros problèmes dans mon esprit.

  • Cela rend les tests unitaires problématiques. L'état mondial peut être contaminé d'un test à l'autre

  • Il applique la règle stricte "Un et un seul", ce qui, même s'il ne pouvait pas changer, le fait soudainement. Un tas de code utilitaire qui utilisait l'objet accessible globalement doit ensuite être modifié.

Cela dit, la plupart des systèmes ont besoin de Big Global Objects. Ce sont des éléments volumineux et coûteux (par exemple, les gestionnaires de connexion de base de données), ou qui contiennent des informations d'état omniprésentes (par exemple, des informations de verrouillage).

L'alternative à un singleton consiste à créer ces gros objets globaux au démarrage et à les transmettre en tant que paramètres à toutes les classes ou méthodes qui ont besoin d'accéder à cet objet.

Le problème ici est que vous vous retrouvez avec un gros jeu de "passe-le-colis". Vous avez un graphique des composants et de leurs dépendances, et certaines classes créent d'autres classes, et chacune doit contenir un tas de composants de dépendance juste parce que leurs composants générés (ou les composants des composants générés) en ont besoin.

Vous rencontrez de nouveaux problèmes de maintenance. Un exemple: tout à coup, votre "WidgetFactory", composant au plus profond du graphique a besoin d'un objet timer que vous souhaitez simuler. Cependant, "WidgetFactory" est créé par "WidgetBuilder" qui fait partie de "WidgetCreationManager", et vous devez avoir trois classes connaissant cet objet timer même si une seule l'utilise réellement. Vous vous retrouvez à vouloir abandonner et revenir à Singletons, et juste rendre cet objet timer globalement accessible.

Heureusement, c'est exactement le problème qui est résolu par un cadre d'injection de dépendance. Vous pouvez simplement indiquer au framework les classes dont il a besoin pour créer, et il utilise la réflexion pour comprendre le graphique de dépendance pour vous, et construit automatiquement chaque objet quand il en a besoin.

Donc, en résumé, les singletons sont mauvais, et l'alternative est d'utiliser un cadre d'injection de dépendance.

Il se trouve que j'utilise Castle Windsor, mais vous avez l'embarras du choix. Voir cette page à partir de 2008 pour une liste des frameworks disponibles.

9
Andrew Shepherd

Tout d'abord, pour que l'injection de dépendance soit "avec état", vous devez utiliser des singletons, donc les gens qui disent que c'est en quelque sorte une alternative se trompent. Les gens utilisent des objets de contexte global tout le temps ... Même l'état de session par exemple est par essence une variable globale. Tout passer, que ce soit par injection de dépendance ou non, n'est pas toujours la meilleure solution. Je travaille actuellement sur une très grande application qui utilise beaucoup d'objets de contexte global (singletons injectés via un conteneur IoC) et cela n'a jamais été un problème de débogage. Surtout avec une architecture pilotée par les événements, il peut être préférable d'utiliser des objets de contexte global plutôt que de faire circuler ce qui a changé. Cela dépend de qui vous demandez.

Tout peut être abusé et cela dépend aussi du type d'application. L'utilisation de variables statiques par exemple dans une application Web est complètement différente d'une application de bureau. Si vous pouvez éviter les variables globales, faites-le, mais elles ont parfois leur utilité. Assurez-vous à tout le moins que vos données globales se trouvent dans un objet contextuel clair. En ce qui concerne le débogage, rien qu'une pile d'appels et certains points d'arrêt ne peuvent résoudre.

Je tiens à souligner que l'utilisation aveugle de variables globales est une mauvaise idée. Les fonctions doivent être réutilisables et ne pas se soucier de la provenance des données - la référence aux variables globales couple la fonction à une entrée de données spécifique. C'est pourquoi il doit être transmis et pourquoi l'injection de dépendances peut être utile, bien que vous ayez toujours affaire à un magasin de contexte centralisé (via des singletons).

Btw ... Certaines personnes pensent que l'injection de dépendance est mauvaise, y compris le créateur de Linq, mais cela n'empêchera pas les gens de l'utiliser, y compris moi-même. En fin de compte, l'expérience sera votre meilleur professeur. Il y a des moments pour suivre les règles et des temps pour les enfreindre.

8
KingOfHypocrites

Étant donné que d'autres réponses ici rendent la distinction entre mutable et immuable globale état, je voudrais dire que même immuable les variables/paramètres globaux sont souvent une gêne.

Considérez la question:

... Disons que j'ai besoin d'une configuration globale pour mon application, par exemple des chemins de dossier système ou des informations d'identification de base de données à l'échelle de l'application. ...

Bon, pour un petit programme, ce n'est probablement pas un problème, mais dès que vous le faites avec des composants dans un système légèrement plus grand , écriture automatisée les tests deviennent soudainement plus difficiles car tous les tests (~ exécutés dans le même processus) doivent fonctionner avec la même valeur de configuration globale.

Si toutes les données de configuration sont transmises explicitement, les composants deviennent beaucoup plus faciles à tester et vous n'avez jamais à vous soucier comment bootstrap une valeur de configuration globale pour plusieurs tests, peut-être même en parallèle.

5
Martin Ba

Pourquoi Global State est-il si mal?

Mutable l'état global est mauvais parce qu'il est très difficile pour notre cerveau de prendre en compte plus de quelques paramètres à la fois et de comprendre comment ils se combinent à la fois dans une perspective temporelle et une perspective de valeur pour affecter quelque chose.

Par conséquent, nous sommes très mauvais pour déboguer ou tester un objet dont le comportement a plus de quelques raisons externes à modifier pendant l'exécution d'un programme. Encore moins quand nous devons raisonner sur des dizaines de ces objets pris ensemble.

3
guillaume31

L'état est inévitable dans toute application réelle. Vous pouvez le terminer comme vous le souhaitez, mais une feuille de calcul doit contenir des données dans les cellules. Vous pouvez créer des objets de cellule avec uniquement des fonctions d'interface, mais cela ne limite pas le nombre d'emplacements pouvant appeler une méthode sur la cellule et modifier les données. Vous créez des hiérarchies d'objets entiers pour essayer de masquer les interfaces afin que d'autres parties du code ne peuvent pas modifier les données par défaut. Cela n'empêche pas une référence arbitraire à l'objet contenant. Rien de tout cela n'élimine les problèmes de concurrence par lui-même. Cela rend plus difficile la prolifération de l'accès aux données, mais cela n'élimine pas réellement les problèmes perçus avec les globaux. Si quelqu'un veut modifier un morceau d'état, il va le faire qu'il soit global ou via une API complexe (la dernière ne fera que décourager, pas empêcher).

La vraie raison de ne pas utiliser le stockage global est d'éviter les collisions de noms. Si vous chargez plusieurs modules qui déclarent le même nom global, vous avez soit un comportement indéfini (très difficile à déboguer car les tests unitaires passeront), soit une erreur de l'éditeur de liens (je pense que C - votre éditeur de liens vous avertit-il ou échoue-t-il?).

Si vous voulez réutiliser du code, vous devez pouvoir récupérer un module à un autre endroit et ne pas le faire accidentellement marcher sur votre global car ils en ont utilisé un avec le même nom. Ou si vous êtes chanceux et obtenez une erreur, vous ne voulez pas avoir à modifier toutes les références dans une section de code pour éviter la collision.

3
phkahler

Les langages conçus pour la conception de systèmes sécurisés et robustes se débarrassent souvent de l'état global mutable . (Cela signifie sans doute pas de globaux, car les objets immuables ne sont en quelque sorte pas vraiment dynamiques car ils n'ont jamais de transition d'état.)

Joe-E est un exemple, et David Wagner explique la décision ainsi:

L'analyse de qui a accès à un objet et le principe du moindre privilège sont tous deux inversés lorsque les capacités sont stockées dans des variables globales et sont donc potentiellement lisibles par n'importe quelle partie du programme. Une fois qu'un objet est globalement disponible, il n'est plus possible de limiter la portée de l'analyse: l'accès à l'objet est un privilège qui ne peut être refusé à aucun code du programme. Joe-E évite ces problèmes en vérifiant que la portée globale ne contient aucune capacité, uniquement des données immuables.

Donc, une façon d'y penser est

  1. La programmation est un problème de raisonnement distribué. Les programmeurs sur de grands projets doivent diviser le programme en morceaux qui peuvent être motivés par des individus.
  2. Plus la portée est petite, plus il est facile de raisonner. Cela est vrai à la fois des individus et des outils d'analyse statique qui tentent de prouver les propriétés d'un système, et des tests qui doivent tester les propriétés d'un système.
  3. Des sources d'autorité importantes qui sont disponibles à l'échelle mondiale rendent les propriétés du système difficiles à raisonner.

Par conséquent, un état mutable à l'échelle mondiale rend plus difficile

  1. concevoir des systèmes robustes,
  2. plus difficile à prouver les propriétés d'un système, et
  3. il est plus difficile d'être sûr que vos tests sont testés dans un domaine similaire à votre environnement de production.

L'état mutable global est similaire à enfer DLL . Au fil du temps, différents morceaux d'un grand système nécessiteront un comportement subtilement différent des morceaux partagés d'état mutable. La résolution de DLL enfer et incohérences d'état mutables partagées nécessite une coordination à grande échelle entre des équipes disparates. Ces problèmes ne se produiraient pas si l'état global avait été correctement défini au départ.

3
Mike Samuel

Eh bien, pour commencer, vous pouvez rencontrer exactement le même problème que vous pouvez avec les singletons. Ce qui ressemble aujourd'hui à "une chose globale dont je n'ai besoin que d'une" se transformera soudainement en quelque chose dont vous aurez plus besoin en cours de route.

Par exemple, aujourd'hui, vous créez ce système de configuration global car vous souhaitez une configuration globale pour l'ensemble du système. Quelques années plus tard, vous portez vers un autre système et quelqu'un dit "hé, vous savez, cela pourrait mieux fonctionner s'il y avait une configuration globale générale et une configuration spécifique à la plate-forme". Soudain, vous avez tout ce travail pour rendre vos structures globales non globales, vous pouvez donc en avoir plusieurs.

(Ce n'est pas un exemple aléatoire ... cela s'est produit avec notre système de configuration dans le projet dans lequel je suis actuellement.)

Étant donné que le coût de fabrication de quelque chose de non global est généralement insignifiant, il est stupide de le faire. Vous créez simplement des problèmes futurs.

3
Gort the Robot

L'autre problème est curieusement qu'elles rendent une application difficile à mettre à l'échelle car elles ne sont pas assez "globales". La portée d'une variable globale est le processus.

Si vous souhaitez faire évoluer votre application en utilisant plusieurs processus ou en exécutant sur plusieurs serveurs, vous ne pouvez pas. Du moins pas avant d'avoir pris en compte tous les globaux et les avoir remplacés par un autre mécanisme.

3
James Anderson

Les globaux ne sont pas si mauvais. Comme indiqué dans plusieurs autres réponses, le vrai problème avec elles est que quel est, aujourd'hui, votre chemin d'accès au dossier global peut, demain, être l'un des nombreux, voire des centaines. Si vous écrivez un programme rapide et unique, utilisez les globaux si c'est plus facile. En règle générale, cependant, autoriser les multiples même si vous pensez que vous n'en avez besoin que d'une est la voie à suivre. Ce n'est pas agréable d'avoir à restructurer un grand programme complexe qui a soudainement besoin de parler à deux bases de données.

Mais ils ne nuisent pas à la fiabilité. Toutes les données référencées à de nombreux endroits de votre programme peuvent provoquer des problèmes si elles changent de façon inattendue. Les énumérateurs s'étouffent lorsque la collection qu'ils énumèrent est modifiée au milieu de l'énumération. Les événements de file d'attente d'événements peuvent se jouer des tours. Les discussions peuvent toujours provoquer des havoks. Tout ce qui n'est pas une variable locale ou un champ immuable est un problème. Les globaux sont ce genre de problème, mais vous n'allez pas y remédier en les rendant non globaux.

Si vous êtes sur le point d'écrire dans un fichier et que le chemin d'accès au dossier change, la modification et l'écriture doivent être synchronisées. (Comme l'une des mille choses qui pourraient mal se passer, disons que vous prenez le chemin, puis ce répertoire est supprimé, puis le chemin du dossier est changé en un bon répertoire, puis vous essayez d'écrire dans le répertoire supprimé.) Le problème existe si le chemin du dossier est global ou est l'un des mille que le programme utilise actuellement.

Il existe un réel problème avec les champs accessibles par différents événements d'une file d'attente, différents niveaux de récursivité ou différents threads. Pour faire simple (et simpliste): les variables locales sont bonnes et les champs sont mauvais. Mais les anciens globaux vont toujours être des champs, donc ce problème (aussi important soit-il) ne s'applique pas au statut Bon ou Mauvais des champs Globaux.

Ajout: Problèmes de multithreading:

(Notez que vous pouvez rencontrer des problèmes similaires avec une file d'attente d'événements ou des appels récursifs, mais le multithreading est de loin le pire.) Considérez le code suivant:

if (filePath != null)  text = filePath.getName();

Si filePath est une variable locale ou une sorte de constante, votre programme ne va pas échouer lors de l'exécution parce que filePath est nul. Le chèque fonctionne toujours. Aucun autre thread ne peut changer sa valeur. Sinon , il n'y a aucune garantie. Quand j'ai commencé à écrire des programmes multithread en Java, j'ai toujours eu des NullPointerExceptions sur des lignes comme celle-ci. Tout autre thread peut changer la valeur à tout moment, et c'est souvent le cas. Comme le soulignent plusieurs autres réponses, cela crée de sérieux problèmes de test. La déclaration ci-dessus peut fonctionner un milliard de fois, l'obtenir à travers des tests approfondis et complets, puis exploser une fois en production. Les utilisateurs ne seront pas en mesure de reproduire le problème, et cela ne se reproduira pas tant qu'ils ne se seront pas convaincus qu'ils voyaient des choses et l'avaient oublié.

Les globaux ont certainement ce problème, et si vous pouvez les éliminer complètement ou les remplacer par des constantes ou des variables locales, c'est une très bonne chose. Si vous avez du code sans état exécuté sur un serveur Web, vous le pouvez probablement. En règle générale, tous vos problèmes de multithreading peuvent être pris en charge par la base de données.

Mais si votre programme doit se souvenir des choses d'une action utilisateur à l'autre, vous aurez des champs accessibles par tous les threads en cours d'exécution. Passer d'un global à un tel domaine non global n'aidera pas la fiabilité.

2
RalphChapin

Je ne vais pas dire si les variables globales sont bonnes ou mauvaises, mais ce que je vais ajouter à la discussion est de dire que si vous n'utilisez pas l'état global, vous perdez probablement beaucoup de mémoire, surtout lorsque vous utilisez classess pour stocker leurs dépendances dans des champs.

Pour l'État mondial, il n'y a pas un tel problème, tout est mondial.

Par exemple: imaginez un scénario suivant: Vous avez une grille 10x10 qui est faite de "Board" et de "Tile" sans classe.

Si vous voulez le faire de la manière OOP vous passerez probablement l'objet "Board" à chaque "Tile". Disons maintenant que "Tile" a 2 champs de type "byte" stockant ses coordonnées . La mémoire totale qu'il faudrait sur une machine 32 bits pour une tuile serait de (1 + 1 + 4 = 6) octets: 1 pour x coord, 1 pour y coord et 4 pour un pointeur vers la carte. Cela donne un total de 600 octets pour la configuration de 10 x 10 tuiles

Maintenant, dans le cas où la carte est de portée globale, un seul objet accessible à partir de chaque tuile ne devrait obtenir que 2 octets de mémoire par chaque tuile, c'est-à-dire les octets de coordonnées x et y. Cela ne donnerait que 200 octets.

Donc, dans ce cas, vous obtenez 1/3 de l'utilisation de la mémoire si vous n'utilisez que l'état global.

Ceci, entre autres choses, je suppose que c'est une raison pour laquelle la portée globale reste toujours dans des langages de niveau (relativement) bas comme C++

1
luke1985

Lorsqu'il est facile de voir et d'accéder à tout l'état global, les programmeurs finissent invariablement par le faire. Ce que vous obtenez est tacite et difficile à suivre les dépendances (int blahblah signifie que le tableau foo est valide dans tout). Essentiellement, il est presque impossible de maintenir les invariants du programme car tout peut être manipulé indépendamment. someInt a une relation entre otherInt, qui est difficile à gérer et plus difficile à prouver si vous pouvez changer directement l'un ou l'autre à tout moment.

Cela dit, cela peut être fait (il y a longtemps, c'était le seul moyen dans certains systèmes), mais ces compétences sont perdues. Ils tournent principalement autour des conventions de codage et de dénomination - le domaine a évolué pour une bonne raison. Votre compilateur et votre éditeur de liens font un meilleur travail de vérification des invariants dans les données protégées/privées des classes/modules que de compter sur les humains pour suivre un plan directeur et lire la source.

1
anon

Il y a plusieurs facteurs à considérer avec l'état global:

  1. Espace mémoire du programmeur
  2. Globales immuables/écrire une fois les globaux.
  3. Mondiaux mutables
  4. Dépendance vis-à-vis des globaux.

Plus vous avez de globaux, plus vous avez de chances d'introduire des doublons, et donc de casser des choses lorsque les doublons sont désynchronisés. Garder tous les globaux dans votre mémoire humaine faillible devient à la fois nécessaire et douloureux.

Immutables/écriture une fois sont généralement OK, mais attention aux erreurs de séquence d'initialisation.

Les globaux mutables sont souvent confondus avec des globaux immuables…

Une fonction qui utilise efficacement les globaux a des paramètres supplémentaires "cachés", ce qui rend la refactorisation plus difficile.

L'état mondial n'est pas mauvais, mais il a un coût certain - utilisez-le lorsque les avantages l'emportent sur les coûts.

0
jmoreno