BobDalgleish já observou que esse padrão (anti-) é chamado de " Dados de acampamento ".
Na minha experiência, a causa mais comum de dados excessivos de tramp é ter um monte de variáveis de estado vinculadas que devem ser realmente encapsuladas em um objeto ou em uma estrutura de dados. Às vezes, pode até ser necessário aninhar um monte de objetos para organizar adequadamente os dados.
Por exemplo, considere um jogo que tenha um personagem de jogador personalizável, com propriedades como playerName
, playerEyeColor
e assim por diante. É claro que o jogador também tem uma posição física no mapa do jogo e várias outras propriedades, como, por exemplo, nível de vida atual e máximo, e assim por diante.
Em uma primeira iteração de tal jogo, pode ser uma escolha perfeitamente razoável transformar todas essas propriedades em variáveis globais - afinal, há apenas um jogador, e quase tudo no jogo envolve o jogador de alguma forma. Então, 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 alterar esse design, talvez porque deseja adicionar um modo multijogador 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 determinada ação em seu jogo pode envolver uma cadeia de chamadas de função como, por exemplo:
mainGameLoop()
-> processInputEvent()
-> doPlayerAction()
-> movePlayer()
-> checkCollision()
-> interactWithNPC()
-> interactWithShopkeeper()
... e a função interactWithShopkeeper()
faz o lojista endereçar o jogador pelo nome, então você de repente precisa passar playerName
como dados de tramp através de todas essas funções. E, claro, se o lojista acha que os jogadores de olhos azuis são ingênuos e cobrará preços mais altos por eles, então você precisaria passar playerEyeColor
por toda a cadeia de funções, e assim por diante.
A solução correta , neste caso, é claro para definir um objeto jogador que encapsula o nome, cor dos olhos, posição, estado de 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 player.
Além disso, várias das funções acima poderiam ser naturalmente feitas em métodos daquele objeto jogador, que automaticamente lhes daria acesso às propriedades do jogador. De certa forma, isso é apenas 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 mais natural se usado corretamente.
É claro que 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 acontece, e uma lista de personagens que não são jogadores movendo-se no mapa, e talvez itens colocados nele, e assim por diante. Você poderia passar todos aqueles ao redor como objetos tramp também, mas isso iria novamente atrapalhar 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 tenham relacionamentos permanentes ou temporários. Assim, por exemplo, o objeto jogador (e provavelmente qualquer objeto NPC também) provavelmente deveria 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)
fizesse não precisa ser explicitamente dado o mapa como um parâmetro.
Da mesma forma, se nosso personagem tivesse, digamos, um cachorro de estimação que os seguisse, agruparíamos naturalmente todas as variáveis de estado que descrevem o cachorro em um único objeto e daríamos ao objeto jogador uma referência ao cachorro (para que o jogador pode, digamos, chamar o cachorro pelo nome) e vice-versa (para que o cão saiba onde o jogador está). E, é claro, nós provavelmente quereríamos fazer com que o jogador e o cachorro obtivessem as duas subclasses de um objeto "ator" mais genérico, para que pudéssemos reutilizar o mesmo código para, digamos, mover ambos pelo mapa.
Ps. Embora eu tenha usado um jogo como exemplo, existem outros tipos de programas em que tais problemas surgem também. 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 querem ser agrupadas em um ou mais objetos interligados. Se os "dados de tramp" intrusivos em suas funções consistem em configurações de opção "globais" ou consultas de banco de dados em cache ou vetores de estado em uma simulação numérica, a solução é invariavelmente identificar o contexto natural ao qual os dados pertencem e faça isso em um objeto (ou qualquer que seja o equivalente mais próximo em seu idioma escolhido).