Fino a poco tempo fa ero un grande fan delle variabili globali perché sono semplici e consentono di creare rapidamente soluzioni ai problemi.
A un certo punto ho dovuto apportare una leggera modifica alla mia applicazione: era necessario recuperare i dati da più fonti.
Ho avuto un paio di semplici funzioni globali come RetrieveUser(id string)
e FindUserByName(name string)
che utilizzava un oggetto globale db
. Si è scoperto che per fare ciò, dovevo riscrivere la logica del database e aggiornare tutte le chiamate alla funzione db.
Ora ho una struttura UsersRepo
con un campo db
che può recuperare i dati da qualsiasi luogo.
Questo è abbastanza ovvio per la maggior parte dei programmatori, tuttavia per qualche motivo non ho mai capito i vantaggi di questo approccio fino ad ora.
Quindi ho deciso di andare oltre e di cercare altri modi per migliorare i miei programmi.
Mi sono imbattuto in questo articolo:
Uno dei punti che fa è che lo stato globale è malvagio e non necessario, e tutte le dipendenze dovrebbero essere passate come argomenti di funzione.
Con cosa sostituiamo i globals? Oggetti "contestuali" globali. Ecco un esempio della libreria OpenGL di stdlib di Go che si sposta da globali a un contesto:
La mia domanda riguarda questi oggetti di contesto.
Ad esempio, in una partita invece di una variabile globale pos
che memorizza la posizione del giocatore avremmo un oggetto contesto Game
che creiamo una volta e passiamo ovunque:
// Using globals
var (
pos int
velocity int
)
func move_player() {
pos += velocity
}
// Using global context object
type Game struct {
pos int
velocity int
...
}
func (game *Game) move_player() {
game.pos += game.velocity
}
Ma sento che è quasi la stessa cosa. Stiamo modificando lo stato globale in move_player()
e non è ovvio che la funzione lo stia facendo.
L'unico vantaggio che posso vedere è che ora possiamo avere più oggetti di gioco nel caso in cui abbiamo bisogno di risolvere un problema simile a quello che ho descritto all'inizio.
Credo che questa sia una limitazione di tutte le lingue imperative. È molto facile scrivere funzioni con effetti collaterali, ed è particolarmente brutto in Go, dove a differenza di C / C ++ non hai puntatori / riferimenti const, quindi se stai passando un puntatore a un oggetto, non puoi mai essere sicuro di aver vinto essere modificato.
Quindi, se stessimo progettando una nuova lingua, oltre alla completa programmazione funzionale, quale sarebbe il modo di risolverlo?
O è semplicemente inevitabile in un linguaggio imperativo?
Voglio dire che potremmo rendere pura la move_player()
func (game *Game) update_pos() int { return game.pos + game.velocity }
ma la posizione deve ancora essere modificata da qualche parte.
Scusa se questo non ha senso. Questo è qualcosa su cui ho riflettuto molto recentemente e non sono riuscito a trovare una risposta.
Grazie.