Mi sto allontanando dall'uso di variabili globali dopo 7 anni, ho bisogno di aiuto per capire le basi

2

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:

link

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:

link

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.

    
posta Alex 14.07.2018 - 08:52
fonte

1 risposta

6

Qui stai mescolando due cose: accessibilità e mutabilità delle variabili.

Ovviamente func (game *Game) move_player() andrà a mutare lo stato di un oggetto. Questo è l'intero punto della funzione. Il movimento significa cambiare posizione. Il punto di iniezione dell'oggetto da mutare è che solo questo oggetto è mutato.

I feel like this is a limitation of all imperative languages. It's very easy to write functions with side effects, and it's especially bad in Go where unlike C/C++ you don't have const pointers/references, so if you are passing a pointer to a an object, you can never be sure it won't be modified.

Il vero problema è che Go non ha, per es. classi come C ++ con membri privati per incapsulare lo stato di un oggetto e impedirne l'accesso al di fuori della classe. In questo modo è più ovvio che i metodi potrebbero cambiare lo stato dell'oggetto, perché non c'è altro modo di accedervi. Infatti, se alcuni metodi non accedono allo stato privato, probabilmente dovrebbero essere static .

Nel tuo esempio, avresti una classe Player e chiamando il suo metodo move() sarebbe solo (e potrebbe solo) modificare lo stato di quell'oggetto, cioè la sua variabile privata. È perfetto, perché è quello che dovrebbe fare questo metodo.

Non è necessario progettare una nuova lingua. Molte lingue là fuori hanno caratteristiche che risolvono i problemi che hai con Go.

    
risposta data 14.07.2018 - 13:20
fonte

Leggi altre domande sui tag