As I know, on one hand, the main stream opinion about global variable is, it is eval[sic] because it may make the program unpredictable : some unknown class or functions can change the value of the global variable even if they don't need to do so...
Questo fa parte della motivazione per evitare variabili globali, ma non è l'intera storia. È vero che le variabili globali hanno un ambito troppo ampio, così che praticamente tutto il codice nel programma ha accesso ad esse.
Tuttavia, un altro problema ha a che fare con l'istanziazione. Non possiamo controllare l'istanziazione di variabili globali tranne che (ri) avviando il programma.
Confrontiamo le funzioni connesse dallo stato globale con oggetti che hanno metodi connessi per stato oggetto.
Poiché non possiamo creare un'istanza dello stato globale, non possiamo avere più di una unità di funzionalità quando utilizziamo funzioni collegate da uno stato globale - mentre con gli oggetti, quando ne hai bisogno, lo crei; se ne hai bisogno ne crei un'altra!
Parti completamente separate del codice possono facilmente condividere il codice (implementazione) eseguendo ogni istanza del proprio oggetto secondo necessità. Usando le funzioni collegate dallo stato globale, ciascuna di queste parti separate del codice rischia di interferire con le altre.
Ciò si estende, partendo da parti separate del codice che possono essere eseguite in momenti diversi o addirittura vicini allo stesso tempo, per separare i thread. Poiché non è possibile creare un'istanza di più copie dello stato globale, lo stato globale tende a ostacolare la programmazione multi-thread, mentre uno strumento che utilizziamo per il threading sicuro consiste nell'istanziare oggetti separati per ogni thread.
Molte unità di funzionalità codificate come funzioni collegate dallo stato globale tendono a portare a spaghetti e le classi tendono a ridurre il disordine misto.
L'astrazione è uno dei nostri migliori strumenti per migliorare la manutenibilità e l'amp; longevità del codice riducendo al contempo il debito tecnico.
Raggruppando funzioni e stati insieme in oggetti incapsulati, OOP incoraggia i programmatori a concentrarsi sulla creazione di astrazioni: sulla progettazione dell'interfaccia (ad esempio l'astrazione), sulla separazione delle interfacce dall'implementazione, sul potenziale per implementazioni multiple di un'interfaccia e su la capacità di istanziare le astrazioni utilizzando varie implementazioni e, naturalmente, più istanze della stessa implementazione. Lo stato globale non incoraggia nessuno di questi.
ShowInfoWindow doesn't need to access user.userId, but it can do so actually.
Un approccio per affrontare questo è stabilire le giuste astrazioni. È possibile che tu abbia diverse astrazioni mescolate insieme, che dovrebbero essere divise in astrazioni indipendenti.
Un altro approccio consiste nell'utilizzare l'incapsulamento, come quando utilizziamo i campi di istanza private
. I campi privati non sono accessibili al di fuori dell'implementazione dell'oggetto e sono quindi più stabili. Dovremmo concentrarci sull'interfaccia dell'astrazione, piuttosto che fornire l'accesso diretto ai dettagli di implementazione interna.
E l'ennesimo approccio consiste nell'utilizzare il più possibile l'immutabilità. I campi immutabili sono totalmente stabili e più facili da ragionare.
but according to the "spirit" of "avoid global variables", isn't passing a object also bad?
Non di solito. Generalmente è meno soggetto a errori e più facile da ragionare su una buona astrazione piuttosto che su int e stringhe abbinate. Una buona astrazione lega insieme int e string in un unico concetto, e questa associazione è più difficile da mescolare e abbinare accidentalmente.
Considera un codice che ha a che fare con due cose invece di una. Ad esempio, codifica & decodificare. Se i clienti devono trattare separatamente ciascuna di queste implementazioni come una coppia, è possibile utilizzare accidentalmente il decodificatore sbagliato per una codifica o viceversa. Considerando che se presentiamo una singola astrazione che lega insieme una codifica & decodificare, il client (programmatore che consuma) non mescolerà in modo inappropriato le implementazioni.
Questo potrebbe applicarsi a una situazione che coinvolge più utenti se, per esempio, è necessario un codice necessario per gestire due utenti allo stesso tempo. Se il client delle astrazioni utilizza le variabili locali name1
e age1
, insieme a name2
e age2
, non ci sarebbe nessun errore (tipo sistema) fornito accoppiando accidentalmente name1
con age2
; l'associazione viene mantenuta implicitamente dal programmatore client - mentre una volta che a name
e age
sono vincolati in un'astrazione user
, l'accoppiamento è esplicito e non c'è possibilità per questo mix accidentale.