BobDalgleish ha già notato che questo modello (anti-) è chiamato " dati del vagabondo ".
Nella mia esperienza, la causa più comune di eccessivi dati sul vagabondo è avere un mucchio di variabili di stato collegate che dovrebbero davvero essere incapsulate in un oggetto o in una struttura di dati. A volte, potrebbe anche essere necessario nidificare un gruppo di oggetti per organizzare correttamente i dati.
Per un semplice esempio, si consideri un gioco che ha un personaggio personalizzabile, con immobili come playerName
, playerEyeColor
e così via. Naturalmente, il giocatore ha anche una posizione fisica sulla mappa del gioco e varie altre proprietà come, diciamo, il livello di salute attuale e massimo e così via.
In una prima iterazione di un gioco del genere, potrebbe essere una scelta perfettamente ragionevole trasformare tutte queste proprietà in variabili globali - dopotutto, c'è un solo giocatore e quasi tutto nel gioco coinvolge in qualche modo il giocatore. Quindi il tuo stato globale potrebbe contenere variabili come:
playerName = "Bob"
playerEyeColor = GREEN
playerXPosition = -8
playerYPosition = 136
playerHealth = 100
playerMaxHealth = 100
Ma ad un certo punto, potresti scoprire che devi cambiare questo design, forse perché vuoi aggiungere una modalità multiplayer al gioco. Come primo tentativo, potresti provare a rendere tutte quelle variabili locali e passandole a funzioni che ne hanno bisogno. Tuttavia, potresti scoprire che un'azione particolare nel tuo gioco potrebbe comportare una catena di chiamate di funzioni come, ad esempio:
mainGameLoop()
-> processInputEvent()
-> doPlayerAction()
-> movePlayer()
-> checkCollision()
-> interactWithNPC()
-> interactWithShopkeeper()
... e la interactWithShopkeeper()
funzione ha il negoziante che indirizza il giocatore per nome, quindi ora devi improvvisamente passare playerName
i dati del vagabondo attraverso tutte quelle funzioni. E, naturalmente, se il negoziante ritiene che i giocatori con gli occhi azzurri siano ingenui e addebitino prezzi più alti per loro, allora dovrai passare playerEyeColor
attraverso l'intera catena di funzioni e così via.
La soluzione corretta , in questo caso, è ovviamente quella di definire un oggetto giocatore che incapsuli il nome, il colore degli occhi, la posizione, la salute e qualsiasi altra proprietà del personaggio del giocatore. In questo modo, devi solo passare quel singolo oggetto a tutte le funzioni che in qualche modo coinvolgono il giocatore.
Inoltre, molte delle funzioni sopra potrebbero essere naturalmente trasformate in metodi dell'oggetto del giocatore, il che darebbe loro automaticamente l'accesso alle proprietà del giocatore. In un certo senso, questo è solo zucchero sintattico, dal momento che la chiamata di un metodo su un oggetto passa effettivamente l'istanza dell'oggetto come parametro nascosto al metodo, ma rende il codice più chiaro e naturale se usato correttamente.
Certo, un gioco tipico avrebbe uno stato molto più "globale" di un semplice giocatore; per esempio, avresti quasi sicuramente una sorta di mappa su cui si svolge il gioco, e un elenco di personaggi non giocanti che si muovono sulla mappa, e forse oggetti posizionati su di essa, e così via. Potresti anche passare tutti quelli come oggetti vagabondi, ma ciò ingombrerebbe nuovamente gli argomenti del tuo metodo.
Invece, la soluzione consiste nel fare in modo che gli oggetti memorizzino i riferimenti a qualsiasi altro oggetto con cui hanno relazioni permanenti o temporanee. Quindi, per esempio, l'oggetto giocatore (e probabilmente anche qualsiasi oggetto NPC) probabilmente dovrebbe memorizzare un riferimento all'oggetto "mondo di gioco", che avrebbe un riferimento al livello / mappa corrente, in modo che un metodo come player.moveTo(x, y)
non abbia bisogno di ricevere esplicitamente la mappa come parametro.
Allo stesso modo, se il nostro personaggio del giocatore avesse, diciamo, un cane da compagnia che li seguiva in giro, raggrupperemmo naturalmente tutte le variabili di stato che descrivono il cane in un singolo oggetto e darebbe al giocatore un riferimento al cane (in modo che il giocatore possa , diciamo, chiama il cane per nome) e viceversa (in modo che il cane sappia dove si trova il giocatore). E, naturalmente, vorremmo probabilmente rendere il giocatore e il cane oggetti entrambe le sottoclassi di un oggetto "attore" più generico, in modo che possiamo riutilizzare lo stesso codice per, diciamo, spostandoci entrambi sulla mappa.
Ps. Anche se ho usato un gioco come esempio, ci sono altri tipi di programmi in cui si presentano anche questi problemi. Nella mia esperienza, tuttavia, il problema di fondo tende ad essere sempre lo stesso: hai un sacco di variabili separate (sia locali che globali) che vogliono davvero essere raggruppate in uno o più oggetti interconnessi. Indipendentemente dal fatto che i "dati vaganti" che si intromettono nelle tue funzioni consistano in impostazioni "globali" delle opzioni o query nella cache memorizzate o vettori di stato in una simulazione numerica, la soluzione è invariabilmente identificare il contesto naturale a cui appartengono i dati e trasformarlo in un oggetto (o qualunque sia l'equivalente più vicino nella lingua scelta).