BobDalgleish ya ha notado que este patrón (anti) se llama " datos de vagabundo ".
En mi experiencia, la causa más común de datos de vagabundeo excesivos es tener un montón de variables de estado vinculadas que realmente deberían encapsularse en un objeto o una estructura de datos. A veces, incluso puede ser necesario anidar un montón de objetos para organizar adecuadamente los datos.
Para un ejemplo simple, considere un juego que tiene un personaje de jugador personalizable, con propiedades como playerName
, playerEyeColor
etc. Por supuesto, el jugador también tiene una posición física en el mapa del juego y varias otras propiedades como, por ejemplo, el nivel de salud actual y máximo, y así sucesivamente.
En una primera iteración de un juego de este tipo, podría ser una opción perfectamente razonable convertir todas estas propiedades en variables globales: después de todo, solo hay un jugador, y casi todo en el juego de alguna manera involucra al jugador. Entonces su estado global puede contener variables como:
playerName = "Bob"
playerEyeColor = GREEN
playerXPosition = -8
playerYPosition = 136
playerHealth = 100
playerMaxHealth = 100
Pero en algún momento, es posible que necesites cambiar este diseño, tal vez porque deseas agregar un modo multijugador al juego. Como primer intento, puede intentar hacer que todas esas variables sean locales y pasarlas a las funciones que las necesitan. Sin embargo, puede encontrar que una acción particular en su juego podría involucrar una cadena de llamada de función como, por ejemplo:
mainGameLoop()
-> processInputEvent()
-> doPlayerAction()
-> movePlayer()
-> checkCollision()
-> interactWithNPC()
-> interactWithShopkeeper()
... y la interactWithShopkeeper()
función hace que el comerciante dirija al jugador por su nombre, por lo que ahora de repente debe pasar playerName
como datos de vagabundo a través de todas esas funciones. Y, por supuesto, si el comerciante cree que los jugadores de ojos azules son ingenuos y cobrarán precios más altos por ellos, entonces necesitará pasar playerEyeColor
por toda la cadena de funciones, y así sucesivamente.
La solución adecuada , en este caso, es, por supuesto, definir un objeto jugador que encapsule el nombre, color de ojos, posición, salud y cualquier otra propiedad del personaje jugador. De esa manera, solo necesita pasar ese único objeto a todas las funciones que de alguna manera involucran al jugador.
Además, varias de las funciones anteriores podrían convertirse naturalmente en métodos de ese objeto jugador, lo que automáticamente les daría acceso a las propiedades del jugador. En cierto modo, esto es solo azúcar sintáctico, ya que llamar a un método en un objeto efectivamente pasa la instancia del objeto como un parámetro oculto al método de todos modos, pero hace que el código se vea más claro y más natural si se usa correctamente.
Por supuesto, un juego típico tendría mucho más estado "global" que solo el jugador; por ejemplo, es casi seguro que tengas algún tipo de mapa en el que se desarrolla el juego, y una lista de personajes que no son jugadores que se mueven en el mapa, y tal vez elementos colocados en él, y así sucesivamente. También podría pasar todos esos objetos vagabundos, pero eso volvería a saturar los argumentos de su método.
En cambio, la solución es hacer que los objetos almacenen referencias a cualquier otro objeto con el que tengan relaciones permanentes o temporales. Entonces, por ejemplo, el objeto jugador (y probablemente también cualquier objeto NPC) probablemente debería almacenar una referencia al objeto "mundo del juego", que tendría una referencia al nivel / mapa actual, de modo que un método como player.moveTo(x, y)
no necesita se le dará explícitamente el mapa como parámetro.
De manera similar, si nuestro personaje jugador tuviera, por ejemplo, un perro mascota que los siguiera, naturalmente agruparíamos todas las variables de estado que describen al perro en un solo objeto, y le daríamos al objeto jugador una referencia al perro (para que el jugador pueda , por ejemplo, llame al perro por su nombre) y viceversa (para que el perro sepa dónde está el jugador). Y, por supuesto, probablemente querríamos que el jugador y el perro se conviertan en subclases de un objeto "actor" más genérico, de modo que podamos reutilizar el mismo código para, por ejemplo, mover ambos por el mapa.
PD. Aunque he usado un juego como ejemplo, hay otros tipos de programas en los que también surgen estos problemas. Sin embargo, en mi experiencia, el problema subyacente tiende a ser siempre el mismo: tiene un montón de variables separadas (ya sean locales o globales) que realmente quieren agruparse en uno o más objetos interconectados. Si los "datos de vagabundo" que se entrometen en sus funciones consisten en configuraciones de opciones "globales" o consultas de bases de datos en caché o vectores de estado en una simulación numérica, la solución es invariablemente identificar el contexto natural al que pertenecen los datos y convertirlo en un objeto (o lo que sea el equivalente más cercano en el idioma elegido).