Perché lo stato globale è così malvagio?

306

Prima di iniziare, lasciatemi dire che sono ben consapevole dei concetti di Astrazione e Iniezione delle Dipendenze. Non ho bisogno che i miei occhi si aprano qui.

Bene, molti di noi dicono (anche) molte volte senza capire veramente "Non usare variabili globali" o "I singleton sono cattivi perché sono globali". Ma cosa veramente è così male per l'infausto stato globale?

Supponiamo di aver bisogno di una configurazione globale per la mia applicazione, ad esempio percorsi di cartelle di sistema o credenziali di database a livello di applicazione.

In questo caso, non vedo alcuna soluzione valida oltre a fornire queste impostazioni in una sorta di spazio globale, che sarà comunemente disponibile per l'intera applicazione.

So che è male abusare , ma lo spazio globale è veramente THAT evil? E se lo è, quali buone alternative ci sono?

    
posta Madara Uchiha 10.05.2012 - 21:35
fonte

17 risposte

262

Molto brevemente, rende lo stato del programma imprevedibile.

Per elaborare, immagina di avere un paio di oggetti che usano entrambi la stessa variabile globale. Supponendo che non si stia utilizzando una fonte di casualità in alcuno dei due moduli, allora l'output di un particolare metodo può essere previsto (e quindi testato) se lo stato del sistema è noto prima di eseguire il metodo.

Tuttavia, se un metodo in uno degli oggetti attiva un effetto collaterale che modifica il valore dello stato globale condiviso, non si sa più quale sia lo stato iniziale quando si esegue un metodo nell'altro oggetto. Ora non è più possibile prevedere quale output si otterrà quando si esegue il metodo e, pertanto, non è possibile testarlo.

A livello accademico, questo potrebbe non sembrare così grave, ma essere in grado di unificare il codice di prova è un passo importante nel processo di dimostrazione della sua correttezza (o almeno di idoneità allo scopo).

Nel mondo reale, questo può avere conseguenze molto gravi. Supponiamo di avere una classe che popola una struttura di dati globale e una classe diversa che consuma i dati in quella struttura di dati, modificandone lo stato o distruggendola nel processo. Se la classe del processore esegue un metodo prima che venga eseguita la classe populator, il risultato è che la classe del processore avrà probabilmente dati incompleti da elaborare e la struttura dei dati su cui stava lavorando la classe della variabile potrebbe essere danneggiata o distrutta. Il comportamento del programma in queste circostanze diventa completamente imprevedibile e probabilmente porterà a perdite epiche.

Inoltre, lo stato globale danneggia la leggibilità del tuo codice. Se il tuo codice ha una dipendenza esterna che non è esplicitamente introdotta nel codice, chiunque abbia il compito di mantenere il tuo codice dovrà cercarlo per capire da dove proviene.

Per quanto riguarda le alternative esistenti, è impossibile non avere affatto uno stato globale, ma in pratica è generalmente possibile limitare lo stato globale a un singolo oggetto che avvolge tutti gli altri e che non deve mai essere referenziato facendo affidamento su le regole di scoping della lingua che stai utilizzando. Se un particolare oggetto ha bisogno di uno stato particolare, allora dovrebbe chiedergli esplicitamente facendolo passare come argomento al suo costruttore o tramite un metodo setter. Questo è noto come Iniezione delle dipendenze.

Può sembrare sciocco passare in un pezzo di stato che è già possibile accedere a causa delle regole di scoping di qualunque lingua tu stia usando, ma i vantaggi sono enormi. Ora, se qualcuno guarda il codice da solo, è chiaro di quale stato ha bisogno e da dove proviene. Ha anche enormi vantaggi per quanto riguarda la flessibilità del modulo di codice e quindi le opportunità per riutilizzarlo in diversi contesti. Se lo stato viene passato e le modifiche allo stato sono locali al blocco di codice, è possibile passare in qualsiasi stato desiderato (se è il tipo di dati corretto) e far sì che il codice lo elabori. Il codice scritto in questo stile tende ad avere l'aspetto di una raccolta di componenti liberamente associati che possono essere facilmente scambiati. Il codice di un modulo non dovrebbe importare da dove proviene lo stato, solo come elaborarlo. Se si passa lo stato in un blocco di codice, quel blocco di codice può esistere in isolamento, non è il caso se si fa affidamento sullo stato globale.

Ci sono molti altri motivi per cui passare lo stato in giro è di gran lunga superiore a fare affidamento sullo stato globale. Questa risposta non è affatto esauriente. Probabilmente potresti scrivere un intero libro sul perché lo stato globale è cattivo.

    
risposta data 10.05.2012 - 21:37
fonte
124

Lo stato globale mutevole è malvagio per molte ragioni:

  • Bug da stato globale mutabile : molti mutevoli bug sono causati dalla mutevolezza. I bug che possono essere causati da una mutazione da qualsiasi parte del programma sono ancora più complicati, dato che è spesso difficile rintracciare la causa esatta
  • Scarsa testabilità : se disponi di uno stato globale modificabile, devi configurarlo per qualsiasi test che scrivi. Questo rende i test più difficili (e le persone che sono persone sono quindi meno propense a farlo!). per esempio. nel caso di credenziali di database a livello di applicazione, cosa succede se un test deve accedere a un database di test specifico diverso da tutto il resto?
  • Inflessibilità : cosa succede se una parte del codice richiede un valore nello stato globale, ma un'altra parte richiede un altro valore (ad esempio, un valore temporaneo durante una transazione)? Improvvisamente hai un po 'di refactoring sulle tue mani
  • Impurità delle funzioni - Le funzioni "pure" (ovvero quelle in cui il risultato dipende solo dai parametri di input e non hanno effetti collaterali) sono molto più facili da ragionare e comporre per creare programmi più grandi. Le funzioni che leggono o manipolano lo stato globale mutabile sono intrinsecamente impure.
  • Comprensione del codice - il comportamento del codice che dipende da molte variabili globali mutevoli è molto più complicato da comprendere - è necessario comprendere l'intervallo di possibili interazioni con la variabile globale prima di poter ragionare sul comportamento del codice . In alcune situazioni, questo problema può diventare intrattabile.
  • Problemi di concorrenza : lo stato globale mutabile richiede in genere una forma di blocco quando viene utilizzato in una situazione concomitante. Questo è molto difficile da ottenere (è una causa di bug) e aggiunge considerevolmente più complessità al tuo codice (difficile / costoso da mantenere).
  • Prestazioni : più thread che colpiscono continuamente lo stesso stato globale causano conflitti di cache e rallentano il sistema in generale.

Alternative allo stato globale mutabile:

  • Parametri di funzione - spesso trascurati, ma parametrizzare meglio le tue funzioni è spesso il modo migliore per evitare lo stato globale. Ti costringe a risolvere l'importante domanda concettuale: quali informazioni richiede questa funzione per svolgere il suo lavoro? A volte ha senso avere una struttura dati chiamata "Contesto" che può essere tramandata in una catena di funzioni che avvolge tutte le informazioni rilevanti.
  • Iniezione di dipendenza - come per i parametri di funzione, appena eseguita un po 'prima (nella costruzione di un oggetto piuttosto che nell'invocazione di funzione). Attenzione però se le tue dipendenze sono oggetti mutabili, questo può causare rapidamente gli stessi problemi dello stato globale mutevole .....
  • Lo stato globale immutabile è in gran parte innocuo: è effettivamente una costante. Ma assicurati che sia davvero una costante e che non sarai tentato di trasformarlo in uno stato globale mutabile in un secondo momento!
  • Singletons immutabili - praticamente lo stesso di uno stato globale immutabile, tranne che puoi rinviare l'istanza finché non sono necessari. Utile per es. grandi strutture dati fisse che richiedono costosi pre-calcoli una tantum. I singleton mutabili sono ovviamente equivalenti allo stato globale mutabile e sono quindi malvagi: -)
  • Associazione dinamica - disponibile solo in alcune lingue come Common Lisp / Clojure, ma in pratica consente di associare un valore all'interno di un ambito controllato (in genere su una base thread-local) che non influisce su altri thread. In una certa misura questo è un modo "sicuro" di ottenere lo stesso effetto di una variabile globale, dal momento che sai che sarà interessato solo il thread corrente di esecuzione. Ciò è particolarmente utile nel caso in cui si abbiano più thread ciascuno che gestisce transazioni indipendenti, ad esempio.
risposta data 11.05.2012 - 03:18
fonte
57
  1. Dal momento che tutta la tua dannata app può essere utilizzata, è sempre incredibilmente difficile includerli di nuovo fuori. Se cambi mai qualcosa a che fare con il tuo globale, tutto il tuo codice deve cambiare. Questo è un mal di testa per la manutenzione: molto più che essere semplicemente in grado di grep per il nome del tipo per scoprire quali funzioni lo usano.
  2. Sono cattivi perché introducono dipendenze nascoste, che interrompono il multithreading, che è sempre più vitale per molte applicazioni.
  3. Lo stato della variabile globale è sempre completamente inaffidabile, perché tutto il tuo codice potrebbe farci qualsiasi cosa.
  4. Sono davvero difficili da testare.
  5. Rendono difficile chiamare l'API. "Devi ricordare di chiamare SET_MAGIC_VARIABLE () prima di chiamare l'API" è solo accattonaggio perché qualcuno dimentichi di chiamarlo. Rende l'utilizzo dell'API incline agli errori, causando bug difficili da trovare. Usandolo come parametro normale, si costringe il chiamante a fornire correttamente un valore.

Basta trasferire un riferimento a funzioni che ne hanno bisogno. Non è così difficile.

    
risposta data 10.05.2012 - 21:42
fonte
32

Se si dice "stato", di solito si intende "stato mutabile". E lo stato globale mutabile è totalmente cattivo, perché significa che qualsiasi parte del programma può influenzare qualsiasi altra parte (cambiando lo stato globale).

Immagina di eseguire il debug di un programma sconosciuto: la funzione A si comporta in un certo modo per determinati parametri di input, ma a volte funziona in modo diverso per gli stessi parametri. Si scopre che utilizza la variabile globale x .

Cerchi posti che modificano x e scopri che ci sono cinque posizioni che lo modificano. Ora buona fortuna scoprire in quali casi la funzione A fa cosa ...

    
risposta data 10.05.2012 - 21:42
fonte
9

Hai risposto alla tua stessa domanda. Sono difficili da gestire quando vengono "abusati", ma possono essere utili e [in qualche modo] prevedibili se usati correttamente da qualcuno che sa come contenerli. La manutenzione e le modifiche a / sui globali sono di solito un incubo, peggiorato quando aumenta la dimensione dell'applicazione.

Programmatori con esperienza che possono distinguere tra le opzioni globali e l'unica soluzione possibile, possono avere problemi minimi nell'utilizzarle. Ma gli infiniti possibili problemi che possono sorgere con il loro uso richiedono il consiglio di non usarli.

modifica: per chiarire cosa intendo, i globali sono imprevedibili per natura. Come per qualsiasi cosa imprevedibile, puoi prendere provvedimenti per contenere l'imprevedibilità, ma ci sono sempre dei limiti a ciò che può essere fatto. Se a questo si aggiunge la seccatura di nuovi sviluppatori che si uniscono al progetto e che devono confrontarsi con variabili relativamente sconosciute, le raccomandazioni sull'uso dei globals dovrebbero essere comprensibili.

    
risposta data 10.05.2012 - 21:47
fonte
8

Prima di tutto per l'iniezione di dipendenze per essere "stateful", avresti bisogno di usare singleton, quindi le persone che dicono che questo è in qualche modo un'alternativa sono errate. Le persone usano sempre gli oggetti di contesto globali ... Anche lo stato di sessione, ad esempio, è essenzialmente una variabile globale. Passare tutto intorno, indipendentemente dall'iniezione di dipendenza o meno, non è sempre la soluzione migliore. Attualmente lavoro su un'applicazione molto grande che utilizza molti oggetti di contesto globali (singletons iniettati tramite un contenitore IoC) e non è mai stato un problema eseguire il debug. Soprattutto con un'architettura basata su eventi, si può preferire l'utilizzo di oggetti di contesto globali e il passaggio di tutto ciò che è cambiato. Dipende da chi chiedi.

Qualsiasi cosa può essere abusata e dipende anche dal tipo di applicazione. L'utilizzo di variabili statiche, ad esempio in un'applicazione Web, è completamente diverso rispetto a un'app desktop. Se puoi evitare le variabili globali, fallo, ma a volte hanno i loro usi. Per lo meno assicurati che i tuoi dati globali siano in un chiaro oggetto contestuale. Per quanto riguarda il debug, nulla può essere risolto da uno stack di chiamate e da alcuni punti di interruzione.

Voglio sottolineare che usare ciecamente le variabili globali è una cattiva idea. Le funzioni dovrebbero essere riutilizzabili e non dovrebbe importare da dove provengono i dati - facendo riferimento alle variabili globali accoppia la funzione con uno specifico input di dati. Questo è il motivo per cui dovrebbe essere passato e perché l'iniezione di dipendenza può essere utile, anche se si sta ancora trattando con un archivio di contesto centralizzato (tramite singleton).

Btw ... Alcune persone pensano che l'iniezione di dipendenza sia negativa, incluso il creatore di Linq, ma questo non impedirà alle persone di usarlo, incluso me stesso. Alla fine l'esperienza sarà la tua migliore insegnante. Ci sono volte a seguire regole e tempi per romperle.

    
risposta data 11.05.2012 - 14:48
fonte
7

Ci sono molti problemi con Singletons - ecco i due problemi più grandi nella mia mente.

  • Rende problematico il test delle unità. Lo stato globale può essere contaminato da un test al successivo

  • Applica la regola "One-and-only-one", che, anche se non può cambiare, improvvisamente accade. Un intero gruppo di codice di utilità che ha utilizzato l'oggetto accessibile a livello globale deve quindi essere modificato.

Detto questo, molti sistemi hanno bisogno di Big Global Objects. Si tratta di elementi che sono grandi e costosi (ad es. Database Connection Manager), o contengono informazioni di stato pervasivo (ad esempio, informazioni di blocco).

L'alternativa a Singleton consiste nell'avere questi Big Global Objects creati all'avvio e passati come parametri a tutte le classi o metodi che necessitano dell'accesso a questo oggetto.

Il problema qui è che si finisce con un grande gioco di "pass-the-parcel". Hai un grafico dei componenti e delle loro dipendenze, e alcune classi creano altre classi, e ognuno deve tenere un gruppo di componenti di dipendenza solo perché i loro componenti spawn (oi componenti dei componenti generati) ne hanno bisogno.

Si verificano nuovi problemi di manutenzione. Un esempio: improvvisamente il componente "WidgetFactory", nel profondo del grafico, ha bisogno di un oggetto timer che si vuole prendere in giro. Tuttavia, "WidgetFactory" è creato da "WidgetBuilder", che fa parte di "WidgetCreationManager", e devi avere tre classi a conoscenza di questo oggetto timer anche se solo uno effettivamente lo usa. Ti ritrovi a voler rinunciare e tornare a Singletons, e rendere questo oggetto timer accessibile a livello globale.

Fortunatamente, questo è esattamente il problema che viene risolto da un framework di Dipendenza Iniezione. Puoi semplicemente dire al framework quali classi deve creare, e usa la reflection per capire il grafico delle dipendenze e automaticamente costruisce ogni oggetto quando è necessario.

Quindi, in sintesi, i singleton sono cattivi, e l'alternativa è usare un framework di dipendenza dalla iniezione.

Mi è capitato di usare Castle Windsor, ma hai solo l'imbarazzo della scelta. Vedi questa pagina dal 2008 per un elenco di framework disponibili.

    
risposta data 11.05.2012 - 08:45
fonte
3

Bene, per esempio, puoi eseguire esattamente lo stesso problema che puoi con i singleton. Quello che oggi sembra una "cosa globale di cui ho bisogno solo uno" si trasformerà improvvisamente in qualcosa di cui hai bisogno in fondo alla strada.

Ad esempio, oggi si crea questo sistema di configurazione globale perché si desidera una configurazione globale per l'intero sistema. Alcuni anni più tardi, ti colleghi ad un altro sistema e qualcuno dice "hey, sai, questo potrebbe funzionare meglio se ci fosse una configurazione globale generale e una configurazione specifica per piattaforma". Improvvisamente hai tutto questo lavoro per rendere le tue strutture globali non globali, quindi puoi averne di più.

(Questo non è un esempio casuale ... è successo con il nostro sistema di configurazione nel progetto in cui mi trovo attualmente.)

Considerando che il costo di realizzare qualcosa di non globale è di solito banale, è sciocco farlo. Stai solo creando problemi futuri.

    
risposta data 10.05.2012 - 22:02
fonte
3

L'altro problema curiosamente è che rendono difficile un'applicazione in scala perché non sono abbastanza "globali". L'ambito di una variabile globale è il processo.

Se si desidera ridimensionare l'applicazione utilizzando più processi o eseguendo su più server non è possibile. Almeno non fino a quando non calcoli tutti i globali e li sostituisci con qualche altro meccanismo.

    
risposta data 11.05.2012 - 03:45
fonte
3

Lingue progettate per sicurezza e amp; la progettazione di sistemi robusti spesso elimina completamente lo stato mutevole globale. (Probabilmente questo significa niente globali, dal momento che gli oggetti immutabili non sono in un certo senso statici dato che non hanno mai una transizione di stato.)

Joe-E è un esempio, e David Wagner spiega il decisione quindi:

Analysis of who has access to an object and the principle of least privilege are both subverted when capabilities are stored in global variables and thus are potentially readable by any part of the program. Once an object is globally available, it is no longer possible to limit the scope of analysis: access to the object is a privilege that cannot be withheld from any code in the program. Joe-E avoids these problems by verifying that the global scope contains no capabilities, only immutable data.

Quindi un modo per pensarci è

  1. La programmazione è un problema di ragionamento distribuito. I programmatori di progetti di grandi dimensioni devono suddividere il programma in parti che possono essere ragionate dai singoli.
  2. Più è piccola la portata, più è facile ragionare. Questo è vero sia per gli individui che per gli strumenti di analisi statici che cercano di dimostrare le proprietà di un sistema e di test che devono testare le proprietà di un sistema.
  3. Fonti significative di autorità che sono disponibili a livello globale rendono difficili da ragionare le proprietà del sistema.

Pertanto, lo stato globalmente mutabile rende più difficile

  1. progettazione di sistemi robusti,
  2. più difficile dimostrare le proprietà di un sistema e
  3. più difficile essere sicuri che i test siano eseguiti in un ambito simile al tuo ambiente di produzione.

stato mutevole globale è simile a DLL hell . Nel tempo, pezzi diversi di un sistema di grandi dimensioni richiedono un comportamento sottilmente diverso da parti condivise dello stato mutabile. Risolvere l'inferno della DLL e le incoerenze dello stato mutabili condivise richiedono un coordinamento su larga scala tra i team disparati. Questi problemi non si verificherebbero se lo stato globale fosse stato definito correttamente all'inizio.

    
risposta data 17.05.2012 - 21:21
fonte
2

I Globali non sono così male. Come affermato in diverse altre risposte, il vero problema con loro è che quello che è, oggi, il tuo percorso di cartella globale potrebbe, domani, essere uno dei tanti, o anche centinaia. Se stai scrivendo un programma rapido e unico, usa le globali se è più facile. In generale, però, tenere conto dei multipli anche quando pensi di averne bisogno solo uno è la strada da percorrere. Non è piacevole dover ristrutturare un grande programma complesso che deve improvvisamente parlare con i due database.

Ma non danneggiano l'affidabilità. Qualsiasi dato a cui si fa riferimento da più punti del programma può causare problemi se cambia in modo imprevisto. Gli enumeratori si strozzano quando la raccolta che stanno enumerando viene modificata a metà dell'enumerazione. Gli eventi della coda degli eventi possono giocare trucchi l'uno sull'altro. Le discussioni possono sempre causare il coraggio. Tutto ciò che non è una variabile locale o un campo non modificabile è un problema. I Globali sono questo tipo di problema, ma non lo sistemerai rendendoli non globali.

Se si sta per scrivere su un file e il percorso della cartella cambia, è necessario sincronizzare la modifica e la scrittura. (Come una delle mille cose che potrebbero andare storte, diciamo che prendi il percorso, poi quella directory viene cancellata, quindi il percorso della cartella è cambiato in una buona directory, quindi provi a scrivere nella directory cancellata.) Il problema esiste se il percorso della cartella è globale o è uno dei mille che il programma sta attualmente utilizzando.

C'è un problema reale con i campi a cui è possibile accedere da diversi eventi su una coda, diversi livelli di ricorsione o diversi thread. Per renderlo semplice (e semplicistico): le variabili locali sono buone e i campi sono cattivi. Ma ex globals saranno ancora campi, quindi questo problema (comunque di importanza fondamentale) non si applica allo stato Buono o Evil dei campi Globali.

Aggiunta: Problemi di multithreading:

(Si noti che si possono avere problemi simili con una coda di eventi o chiamate ricorsive, ma il multithreading è di gran lunga il peggiore.) Considera il seguente codice:

if (filePath != null)  text = filePath.getName();

Se filePath è una variabile locale o un qualche tipo di costante, il tuo programma non fallirà durante l'esecuzione perché filePath è nullo. Il controllo funziona sempre. Nessun altro thread può cambiare il suo valore. Altrimenti , non ci sono garanzie. Quando ho iniziato a scrivere programmi multithread in Java, ho ottenuto NullPointerExceptions su righe come questa tutto il tempo. Qualsiasi altro thread può modificare il valore in qualsiasi momento e spesso lo fanno. Come molte altre risposte sottolineano, questo crea seri problemi per i test. La dichiarazione di cui sopra può funzionare un miliardo di volte, sottoponendola a test completi e completi, per poi saltare in aria una volta in produzione. Gli utenti non saranno in grado di riprodurre il problema e non accadrà di nuovo fino a quando non si saranno convinti di vedere le cose e di averlo dimenticato.

I Globali hanno sicuramente questo problema, e se puoi eliminarli completamente o sostituirli con costanti o variabili locali, questa è una cosa molto buona. Se hai un codice stateless in esecuzione su un server web, probabilmente lo puoi fare. In genere, tutti i problemi di multithreading possono essere assunti dal database.

Ma se il tuo programma deve ricordare le cose da un'azione dell'utente a quella successiva, avrai campi accessibili da qualsiasi thread in esecuzione. Passare da un campo globale a un campo non globale non aiuterà l'affidabilità.

    
risposta data 10.05.2012 - 23:55
fonte
2

Why is Global State so Evil?

Lo stato globale

Mutevole è malvagio perché è molto difficile per il nostro cervello prendere in considerazione più di pochi parametri alla volta e capire come combinano sia una prospettiva temporale che una prospettiva di valore per influire su qualcosa.

Pertanto, siamo molto pessimi nel debug o testare un oggetto il cui comportamento ha più di un motivo esterno per essere modificato durante l'esecuzione di un programma. Per non parlare di quando dobbiamo ragionare su dozzine di questi oggetti presi insieme.

    
risposta data 11.05.2012 - 18:11
fonte
2

Lo stato è inevitabile in qualsiasi applicazione reale. Puoi racchiuderlo come preferisci, ma un foglio di calcolo deve contenere dati nelle celle. È possibile creare oggetti cella con sole funzioni come interfaccia, ma ciò non limita il numero di posti che possono chiamare un metodo sulla cella e modificare i dati. Costruite intere gerarchie di oggetti per cercare di nascondere le interfacce in modo che altre parti del codice non possano modificare i dati per impostazione predefinita. Ciò non impedisce che un riferimento all'oggetto contenitore venga passato in giro arbitrariamente. Nulla di ciò elimina da solo i problemi di concorrenza. Rende più difficile proliferare l'accesso ai dati, ma in realtà non elimina i problemi percepiti con i globali. Se qualcuno vuole modificare un pezzo di stato, lo farà in modo che sia globale o tramite un'API complessa (più tardi scoraggerebbe, non impedire).

La vera ragione per non utilizzare la memoria globale è evitare conflitti di nome. Se carichi più moduli che dichiarano lo stesso nome globale, hai un comportamento indefinito (molto difficile eseguire il debug perché i test delle unità passeranno) o un errore del linker (sto pensando a C - Il tuo linker avvisa o fallisce su questo?).

Se vuoi riutilizzare il codice, devi essere in grado di prendere un modulo da un altro posto e non farlo passare accidentalmente sul tuo globale perché ne ha usato uno con lo stesso nome. Oppure, se sei fortunato e ricevi un errore, non vuoi modificare tutti i riferimenti in una sezione del codice per evitare la collisione.

    
risposta data 11.05.2012 - 19:11
fonte
1

Non dirò se le variabili globali sono buone o cattive, ma quello che aggiungerò alla discussione è dire che se non stai usando lo stato globale, probabilmente stai sprecando molto di memoria, specialmente quando si usano le lezioni per memorizzare le loro dipendenze nei campi.

Per lo stato globale, non esiste questo problema, tutto è globale.

Ad esempio: immagina uno scenario seguente: hai una griglia 10x10 che è composta da classi "Board" e "Tile".

Se vuoi farlo nel modo OOP, probabilmente passerai l'oggetto "Board" a ogni "Tile". Diciamo ora che "Tile" ha 2 campi di tipo "byte" che memorizzano le sue coordinate. La memoria totale che impiegherebbe su una macchina a 32 bit per una piastrella sarebbe (1 + 1 + 4 = 6) byte: 1 per x coord, 1 per y coord e 4 per un puntatore alla scheda. Ciò fornisce un totale di 600 byte per l'installazione di 10x10 tile

Ora, nel caso in cui la scheda sia in ambito globale, un singolo oggetto accessibile da ogni riquadro dovrebbe avere solo 2 byte di memoria per ogni riquadro, cioè i byte di coordinate xey. Questo darebbe solo 200 byte.

Quindi in questo caso si ottiene 1/3 dell'utilizzo della memoria se si utilizza solo lo stato globale.

Questo, oltre ad altre cose, credo sia una ragione per cui l'ambito globale rimane ancora in linguaggi (relativamente) di basso livello come il C ++

    
risposta data 22.03.2014 - 13:51
fonte
1

Poiché alcune altre risposte qui fanno la distinzione tra lo stato globale mutabile e immutabile , vorrei precisare che anche immutabile le variabili / impostazioni globali sono spesso un fastidio .

Considera la domanda:

... Let's say I need a global configuration for my application, for instance system folder paths, or application-wide database credentials. ...

Giusto, per un piccolo programma, questo probabilmente non è un problema, ma non appena lo fai con componenti in un sistema leggermente più grande, scrivere test automatici diventa improvvisamente più difficile perché tutti i test ( ~ in esecuzione nello stesso processo) deve funzionare con lo stesso valore di configurazione globale.

Se tutti i dati di configurazione vengono passati in modo esplicito, i componenti diventano molto più facili da testare e non devi mai preoccuparti di eseguire il bootstrap di un valore di configurazione globale per più test, magari anche in parallelo.

    
risposta data 30.06.2017 - 14:02
fonte
0

Quando è facile vedere e accedere a tutto lo stato globale, i programmatori invariabilmente finiscono per farlo. Quello che ottieni è non detto e difficile da tracciare le dipendenze (int blahblah significa che l'array foo è valido in qualsiasi cosa). In sostanza rende quasi impossibile mantenere invarianti di programma poiché tutto può essere ruotato indipendentemente. someInt ha una relazione tra otherInt, che è difficile da gestire e più difficile da provare se puoi cambiare direttamente in qualsiasi momento.

Detto questo, può essere fatto (via del ritorno quando era l'unico modo in alcuni sistemi), ma quelle abilità sono perse. Riguardano principalmente la codifica e le convenzioni di denominazione - il campo è andato avanti per una buona ragione. Il tuo compilatore e linker fanno un lavoro migliore per controllare gli invarianti nei dati protetti / privati di classi / moduli piuttosto che affidarsi agli umani per seguire un piano generale e leggere la fonte.

    
risposta data 11.05.2012 - 04:50
fonte
0

Ci sono diversi fattori da considerare con lo stato globale:

  1. Spazio memoria programmatore
  2. Globali immutabili / scrivi una volta globali.
  3. Globali mutevoli
  4. Dipendenza dalle informazioni globali.

Più globalmente hai, maggiore è la possibilità di introdurre duplicati e quindi di rompere le cose quando i duplicati sono fuori sincrono. Mantenere tutti i globali nella tua memoria umana falsa diventa sia necessario che doloroso.

Immutables / write una volta sono generalmente OK, ma fai attenzione agli errori nella sequenza di inizializzazione.

Le variabili globali mutevoli vengono spesso scambiate per variabili globali immutabili ...

Una funzione che utilizza efficacemente le globali ha parametri extra "nascosti", rendendo più difficile il refactoring.

Lo stato globale non è malvagio, ma ha un costo definito: utilizzalo quando i benefici superano i costi.

    
risposta data 11.05.2012 - 14:19
fonte

Leggi altre domande sui tag