BobDalgleish já observou que esse (anti-) padrão é chamado de " dados vagabundos ".
Na minha experiência, a causa mais comum de dados excessivos de tramp é ter um monte de variáveis de estado vinculadas que realmente devem ser encapsuladas em um objeto ou uma estrutura de dados. Às vezes, pode até ser necessário aninhar um monte de objetos para organizar adequadamente os dados.
Para um exemplo simples, considere um jogo que tem um personagem personalizável player, com propriedades como playerName
, playerEyeColor
e assim por diante. Obviamente, o jogador também tem uma posição física no mapa do jogo e várias outras propriedades como, por exemplo, o nível de saúde atual e máximo, e assim por diante.
Em uma primeira iteração de um jogo desse tipo, pode ser uma escolha perfeitamente razoável transformar todas essas propriedades em variáveis globais - afinal, existe apenas um jogador e quase tudo no jogo de alguma forma envolve o jogador. Portanto, seu estado global pode conter variáveis como:
playerName = "Bob"
playerEyeColor = GREEN
playerXPosition = -8
playerYPosition = 136
playerHealth = 100
playerMaxHealth = 100
Mas, em algum momento, você pode achar que precisa mudar esse design, talvez porque queira adicionar um modo multiplayer ao jogo. Como primeira tentativa, você pode tentar tornar todas essas variáveis locais e passá-las para funções que precisam delas. No entanto, você pode descobrir que uma ação específica do seu jogo pode envolver uma cadeia de chamadas de funções como, digamos:
mainGameLoop()
-> processInputEvent()
-> doPlayerAction()
-> movePlayer()
-> checkCollision()
-> interactWithNPC()
-> interactWithShopkeeper()
... e a interactWithShopkeeper()
função faz o lojista endereçar o jogador pelo nome, de modo que agora você precisa repentinamente passar playerName
como dados vagabundos por todas essas funções. E, é claro, se o lojista achar que os jogadores de olhos azuis são ingênuos e cobrar preços mais altos por eles, será necessário passar playerEyeColor
por toda a cadeia de funções e assim por diante.
A solução adequada , nesse caso, é definir um objeto de jogador que encapsule o nome, a cor dos olhos, a posição, a saúde e quaisquer outras propriedades do personagem do jogador. Dessa forma, você só precisa passar esse único objeto para todas as funções que de alguma forma envolvem o jogador.
Além disso, várias das funções acima podem ser naturalmente transformadas em métodos desse objeto de jogador, o que lhes daria acesso automaticamente às propriedades do jogador. De certa forma, isso é apenas um açúcar sintático, já que chamar um método em um objeto efetivamente passa a instância do objeto como um parâmetro oculto para o método, mas faz com que o código pareça mais claro e natural se usado corretamente.
Obviamente, um jogo típico teria muito mais estado "global" do que apenas o jogador; por exemplo, você quase certamente teria algum tipo de mapa no qual o jogo ocorre, e uma lista de personagens não-jogadores que se movem no mapa, e talvez itens colocados nele, e assim por diante. Você também pode passar todos esses objetos ao redor como objetos vagabundos, mas isso atrapalhará novamente seus argumentos de método.
Em vez disso, a solução é fazer com que os objetos armazenem referências a quaisquer outros objetos com os quais eles tenham relacionamentos permanentes ou temporários. Assim, por exemplo, o objeto jogador (e provavelmente também qualquer objeto NPC) provavelmente deve armazenar uma referência ao objeto "mundo do jogo", que teria uma referência ao nível / mapa atual, para que um método como player.moveTo(x, y)
esse não precise receber explicitamente o mapa como parâmetro.
Da mesma forma, se o personagem do jogador tivesse, digamos, um cachorro de estimação que os seguisse, naturalmente agruparíamos todas as variáveis de estado que descrevem o cachorro em um único objeto, e forneceríamos ao objeto do jogador uma referência ao cachorro (para que o jogador possa , digamos, chame o cachorro pelo nome) e vice-versa (para que ele saiba onde está o jogador). E, é claro, provavelmente quereríamos fazer com que o jogador e o cachorro objetem as duas subclasses de um objeto "ator" mais genérico, para que possamos reutilizar o mesmo código para, digamos, mover os dois ao redor do mapa.
Ps. Embora eu tenha usado um jogo como exemplo, há outros tipos de programas em que esses problemas também surgem. Na minha experiência, porém, o problema subjacente tende a ser sempre o mesmo: você tem um monte de variáveis separadas (locais ou globais) que realmente desejam agrupar-se em um ou mais objetos interligados. Se os "dados de rastreamento" que se intrometem em suas funções consistem em configurações de opções "globais" ou consultas em banco de dados em cache ou vetores de estado em uma simulação numérica, a solução é sempre identificar o contexto natural ao qual os dados pertencem e transformá-lo em um objeto (ou o equivalente mais próximo no idioma escolhido).