BobDalgleish a déjà noté que ce modèle (anti) est appelé " données de tramp ".
D'après mon expérience, la cause la plus fréquente de données de tramp excessives est la présence d'un ensemble de variables d'état liées qui doivent être réellement encapsulées dans un objet ou une structure de données. Parfois, il peut même être nécessaire d'imbriquer un tas d'objets pour organiser correctement les données.
Pour un exemple simple, considérons un jeu qui a un caractère de joueur personnalisable, avec des propriétés telles que playerName
, playerEyeColor
etc. Bien sûr, le joueur a également une position physique sur la carte du jeu, ainsi que diverses autres propriétés telles que, par exemple, le niveau de santé actuel et maximal, etc.
Lors d'une première itération d'un tel jeu, il peut s'avérer un choix parfaitement raisonnable de transformer toutes ces propriétés en variables globales - après tout, il n'y a qu'un seul joueur et presque tout dans le jeu implique le joueur d'une manière ou d'une autre. Donc, votre état global peut contenir des variables telles que:
playerName = "Bob"
playerEyeColor = GREEN
playerXPosition = -8
playerYPosition = 136
playerHealth = 100
playerMaxHealth = 100
Mais à un moment donné, vous constaterez peut-être que vous devez modifier cette conception, peut-être parce que vous souhaitez ajouter un mode multijoueur au jeu. Dans un premier temps, vous pouvez essayer de rendre toutes ces variables locales et de les transmettre aux fonctions qui en ont besoin. Cependant, vous constaterez peut-être qu'une action particulière de votre jeu peut impliquer une chaîne d'appels de fonctions, par exemple:
mainGameLoop()
-> processInputEvent()
-> doPlayerAction()
-> movePlayer()
-> checkCollision()
-> interactWithNPC()
-> interactWithShopkeeper()
... et la interactWithShopkeeper()
fonction demande au commerçant d’adresser son nom au joueur, de sorte que vous devez maintenant soudainement transmettre des playerName
données clandestines à travers toutes ces fonctions. Et, bien sûr, si le commerçant pense que les joueurs aux yeux bleus sont naïfs et qu'ils leur demanderont des prix plus élevés, vous devrez passer playerEyeColor
par toute la chaîne de fonctions, etc.
La solution appropriée , dans ce cas, consiste bien entendu à définir un objet de joueur qui encapsule le nom, la couleur des yeux, la position, la santé et toutes autres propriétés du personnage de joueur. De cette façon, il vous suffit de transmettre cet objet unique à toutes les fonctions qui impliquent le joueur.
En outre, plusieurs des fonctions ci-dessus pourraient naturellement être transformées en méthodes de cet objet joueur, ce qui leur donnerait automatiquement accès aux propriétés du joueur. D'une certaine manière, il ne s'agit que d'un simple sucre syntaxique, car l'appel d'une méthode sur un objet transmet de toute façon l'instance d'objet en tant que paramètre masqué à la méthode, mais rend le code plus clair et plus naturel s'il est utilisé correctement.
Bien sûr, un jeu typique aurait un état beaucoup plus "global" que celui du joueur; Par exemple, vous avez presque certainement une sorte de carte sur laquelle se déroule le jeu, ainsi qu'une liste des personnages non-joueurs se déplaçant sur la carte, et peut-être des objets placés dessus, etc. Vous pouvez également passer tous ceux qui se trouvent autour de vous comme des objets tramp, mais cela encombrerait encore les arguments de votre méthode.
Au lieu de cela, la solution consiste à laisser les objets stocker des références à tout autre objet avec lequel ils ont des relations permanentes ou temporaires. Ainsi, par exemple, l’objet joueur (et probablement aussi tout objet NPC) devrait probablement stocker une référence à l’objet "world game", qui aurait une référence au niveau / map en cours, de sorte qu’une méthode telle player.moveTo(x, y)
que être explicitement donné la carte en tant que paramètre.
De même, si notre personnage joueur avait, par exemple, un chien qui les suivait, nous regrouperions naturellement toutes les variables d'état décrivant le chien dans un seul objet et donnerions à l'objet joueur une référence au chien (afin que le joueur puisse , appelez le chien par son nom) et vice-versa (pour que le chien sache où se trouve le joueur). Et, bien sûr, nous voudrions probablement que le joueur et les objets chien soient les deux sous-classes d’un objet "acteur" plus générique, afin de pouvoir réutiliser le même code pour déplacer les deux sur la carte, par exemple.
Ps. Même si j'ai utilisé un jeu comme exemple, il existe d'autres types de programmes dans lesquels de tels problèmes se posent également. D'après mon expérience, toutefois, le problème sous-jacent a tendance à être toujours le même: vous avez un ensemble de variables distinctes (qu'elles soient locales ou globales) qui souhaitent réellement être regroupées dans un ou plusieurs objets liés. Que la "donnée tampon" introduite dans vos fonctions se compose de paramètres d'option "globaux" ou de requêtes de base de données en cache ou de vecteurs d'état dans une simulation numérique, la solution consiste invariablement à identifier le contexte naturel auquel les données appartiennent et à en faire un objet. (ou quel que soit l'équivalent le plus proche dans la langue de votre choix).