Il y a beaucoup de raisons pour lesquelles les globaux sont mauvais en POO.
Si le nombre ou la taille des objets à partager est trop important pour être efficacement transmis dans les paramètres de fonction, tout le monde recommande généralement l' injection de dépendances plutôt qu'un objet global.
Cependant, dans le cas où presque tout le monde a besoin de connaître une certaine structure de données, pourquoi l'injection de dépendance est-elle meilleure qu'un objet global?
Exemple (simplifié, pour montrer le point en général, sans plonger trop profondément dans une application spécifique)
Il existe un certain nombre de véhicules virtuels qui ont un grand nombre de propriétés et d'états, du type, du nom, de la couleur, de la vitesse, de la position, etc. Un certain nombre d'utilisateurs peuvent les contrôler à distance, et un grand nombre d'événements (les deux utilisateurs- initiés et automatiques) peuvent changer beaucoup de leurs états ou propriétés.
La solution naïve serait de simplement en faire un conteneur global, comme
vector<Vehicle> vehicles;
qui peut être consulté de n'importe où.
La solution la plus conviviale pour les POO serait que le conteneur soit membre de la classe qui gère la boucle d'événement principale et qu'il soit instancié dans son constructeur. Chaque classe qui en a besoin, et qui est membre du thread principal, aura accès au conteneur via un pointeur dans son constructeur. Par exemple, si un message externe arrive via une connexion réseau, une classe (une pour chaque connexion) gérant l'analyse prendra le relais et l'analyseur aura accès au conteneur via un pointeur ou une référence. Maintenant, si le message analysé entraîne soit un changement dans un élément du conteneur, ou nécessite certaines données pour effectuer une action, il peut être géré sans avoir besoin de lancer des milliers de variables via des signaux et des emplacements (ou pire, les stocker dans l'analyseur pour être récupéré plus tard par celui qui a appelé l'analyseur). Bien sûr, toutes les classes qui reçoivent l'accès au conteneur via l'injection de dépendances font partie du même thread. Différents threads n'y accéderont pas directement, mais feront leur travail, puis enverront des signaux au thread principal, et les emplacements du thread principal mettront à jour le conteneur.
Cependant, si la majorité des classes auront accès au conteneur, qu'est-ce qui le rend vraiment différent d'un global? Si tant de classes ont besoin des données dans le conteneur, la "méthode d'injection de dépendances" n'est-elle pas simplement un global déguisé?
Une réponse serait la sécurité des threads: même si je fais attention à ne pas abuser du conteneur global, peut-être qu'un autre développeur à l'avenir, sous la pression d'une échéance proche, utilisera néanmoins le conteneur global dans un thread différent, sans prendre soin de tout les cas de collision. Cependant, même dans le cas de l'injection de dépendances, on pourrait donner un pointeur à quelqu'un qui s'exécute dans un autre thread, conduisant aux mêmes problèmes.
Réponses:
L'injection de dépendance est la meilleure chose depuis le pain tranché , alors que les objets mondiaux sont connus depuis des décennies pour être la source de tous les maux , c'est donc une question plutôt intéressante.
Le point de l'injection de dépendance n'est pas simplement de s'assurer que chaque acteur qui a besoin d'une ressource peut l'avoir, car évidemment, si vous rendez toutes les ressources globales, alors chaque acteur aura accès à toutes les ressources, problème résolu, non?
Le point d'injection de dépendance est:
Le fait que dans votre configuration particulière tous les acteurs aient besoin d'accéder à la même instance de ressource n'est pas pertinent. Croyez-moi, vous aurez un jour besoin de reconfigurer les choses pour que les acteurs aient accès aux différentes instances de la ressource, et alors vous vous rendrez compte que vous vous êtes peint dans un coin. Certaines réponses ont déjà pointé une telle configuration: le test .
Autre exemple: supposez que vous divisez votre application en client-serveur. Tous les acteurs sur le client utilisent le même ensemble de ressources centrales sur le client et tous les acteurs sur le serveur utilisent le même ensemble de ressources centrales sur le serveur. Supposons maintenant, un jour, que vous décidiez de créer une version "autonome" de votre application client-serveur, où le client et le serveur sont empaquetés dans un seul exécutable et exécutés dans la même machine virtuelle. (Ou environnement d'exécution, selon la langue de votre choix.)
Si vous utilisez l'injection de dépendances, vous pouvez facilement vous assurer que tous les acteurs clients reçoivent les instances de ressource client avec lesquelles travailler, tandis que tous les acteurs serveur reçoivent les instances de ressource serveur.
Si vous n'utilisez pas l'injection de dépendances, vous n'avez pas de chance, car une seule instance globale de chaque ressource peut exister dans une machine virtuelle.
Ensuite, vous devez considérer: tous les acteurs ont-ils vraiment besoin d'accéder à cette ressource? vraiment?
Il est possible que vous ayez fait l'erreur de transformer cette ressource en un objet divin, (donc, bien sûr, tout le monde doit y avoir accès) ou peut-être surestimez-vous grossièrement le nombre d'acteurs de votre projet qui ont réellement besoin d' accéder à cette ressource .
Avec les globaux, chaque ligne de code source dans votre application entière a accès à chaque ressource globale unique. Avec l'injection de dépendance, chaque instance de ressource n'est visible que par les acteurs qui en ont réellement besoin. Si les deux sont les mêmes (les acteurs qui ont besoin d'une ressource particulière comprennent 100% des lignes de code source de votre projet), alors vous devez avoir fait une erreur dans votre conception. Donc, soit
Transformer cette grande et énorme ressource de dieu en sous-ressources plus petites, de sorte que différents acteurs doivent avoir accès à différentes parties de celle-ci, mais rarement un acteur a besoin de toutes ses pièces, ou
Refactorisez vos acteurs pour qu'ils n'acceptent à leur tour que les paramètres des sous-ensembles du problème sur lesquels ils doivent travailler, de sorte qu'ils n'ont pas à consulter en permanence une grande et énorme ressource centrale.
la source
C'est une affirmation douteuse. Le lien que vous utilisez comme preuve fait référence à l' état - mutable globals. Ils sont décidément mauvais. Les globales en lecture seule ne sont que des constantes. Les constantes sont relativement saines. Je veux dire, vous n'allez pas injecter la valeur de pi dans toutes vos classes, n'est-ce pas?
Non, ils ne le font pas.
Si vos fonctions / classes ont trop de dépendances, les gens recommandent de s'arrêter et de voir pourquoi votre conception est si mauvaise. Avoir une cargaison de dépendances est un signe que votre classe / fonction fait probablement trop de choses et / ou que vous n'avez pas d'abstraction suffisante.
Ceci est un cauchemar du design. Vous avez un tas de choses qui peuvent être utilisées / abusées sans aucun moyen de validation, aucune manière de limitations de concurrence - c'est juste un couplage partout.
Oui, avoir un couplage avec des données communes partout n'est pas trop différent d'un monde, et en tant que tel, c'est toujours mauvais.
L'avantage est que vous dissociez les consommateurs de la variable globale elle-même. Cela vous offre une certaine flexibilité dans la façon dont l'instance est créée. Cela ne vous oblige pas non plus à avoir une seule instance. Vous pouvez en faire différents à transmettre à vos différents consommateurs. Vous pouvez également créer différentes implémentations de votre interface par consommateur si vous en avez besoin.
Et en raison du changement qui vient inévitablement avec les exigences changeantes des logiciels, un tel niveau de base de flexibilité est vital .
la source
foo
s"?Il y a trois raisons principales à considérer.
Donc, pour résumer: DI n'est pas un objectif, c'est juste un outil qui permet d'atteindre un objectif. Si vous l'utilisez à mauvais escient (comme il semble que vous le fassiez dans votre exemple), vous ne vous rapprocherez pas du but, il n'y a aucune propriété magique de DI qui rendra une mauvaise conception bonne.
la source
C'est vraiment une question de testabilité - normalement, un objet global est très maintenable et facile à lire / comprendre (une fois que vous le savez, bien sûr) mais c'est un élément permanent et lorsque vous venez de diviser vos classes en tests isolés, vous constatez que votre l'objet global est bloqué en disant "qu'en est-il de moi" et donc vos tests isolés nécessitent que l'objet global y soit inclus. Cela n'aide pas que la plupart des frameworks de test ne prennent pas facilement en charge ce scénario.
Alors oui, c'est toujours un mondial. La raison fondamentale pour l'avoir n'a pas changé, seulement la manière dont il est accédé.
Quant à l'initialisation, c'est toujours un problème - vous devez toujours définir la configuration pour que vos tests puissent s'exécuter, donc vous n'y gagnez rien. Je suppose que vous pouvez diviser un grand objet dépendant en plusieurs plus petits et passer celui qui convient aux classes qui en ont besoin, mais vous obtenez probablement encore plus de complexité pour l'emporter sur les avantages.
Indépendamment de tout cela, c'est un exemple de la `` queue qui remue le chien '', la nécessité de tester conduit à la façon dont la base de code est architecturée (des problèmes similaires se posent avec des éléments tels que les classes statiques, par exemple la date / heure actuelle).
Personnellement, je préfère coller toutes mes données globales dans un objet global (ou plusieurs) mais fournir des accesseurs pour obtenir les bits dont mes classes ont besoin. Ensuite, je peux me moquer de ceux-ci pour retourner les données que je veux quand il s'agit de tester sans la complexité supplémentaire de DI.
la source
En plus des réponses que vous avez déjà,
L'une des caractéristiques de la programmation OOP est de ne conserver que les propriétés dont vous avez vraiment besoin pour un objet spécifique,
MAINTENANT SI votre objet a trop de propriétés - vous devez décomposer davantage votre objet en sous-objets.
Éditer
Déjà mentionné pourquoi les objets globaux sont mauvais, j'y ajouterais un exemple.
Vous devez faire des tests unitaires sur le code GUI d'un formulaire Windows, si vous utilisez global, vous devrez créer tous les boutons et ses événements par exemple pour créer un cas de test approprié ...
la source