Perché le variabili statiche sono considerate malvagie?


635

Sono un programmatore Java che è nuovo nel mondo aziendale. Di recente ho sviluppato un'applicazione usando Groovy e Java. Per tutto il codice che ho scritto ho usato un buon numero di statistiche. Il lotto tecnico senior mi ha chiesto di ridurre il numero di statistiche utilizzate. Ho cercato su Google lo stesso e trovo che molti programmatori sono abbastanza contrari all'utilizzo di variabili statiche.

Trovo le variabili statiche più convenienti da usare. E presumo che siano anche efficienti (per favore, correggimi se sbaglio), perché se dovessi effettuare 10.000 chiamate a una funzione all'interno di una classe, sarei felice di rendere statico il metodo e utilizzare un metodo semplice Class.methodCall()anziché ingombrando la memoria con 10.000 istanze della classe, giusto?

Inoltre, la statica riduce le interdipendenze dalle altre parti del codice. Possono agire come titolari di stato perfetti. Aggiungendo a questo trovo che la statica sia ampiamente implementata in alcune lingue come Smalltalk e Scala . Allora perché questa oppressione per la statica è prevalente tra i programmatori (specialmente nel mondo di Java)?

PS: per favore correggimi se le mie ipotesi sulla statica sono sbagliate.


43
Solo per il gusto di dire, non ci sono variabili o metodi statici su Smalltalk o Scala, proprio perché i metodi e le variabili statici sono contrari ai principi OOP.
Maurício Linhares,

87
Almeno un'affermazione che fai è piuttosto curiosa: "la statica riduce le interdipendenze dalle altre parti del codice". In generale stringono le dipendenze. Il codice in cui viene effettuata la chiamata è strettamente associato al codice chiamato. Nessuna astrazione tra, dipendenza diretta.
Arne Deutsch,

11
Buona domanda ... più di un programmatore.SE domande tho?
WernerCD,

26
Il tuo secondo paragrafo riguarda un argomento completamente diverso, ovvero i metodi statici .
Paul,

8
Anche la programmazione funzionale si acciglia anche sullo stato globale. Se mai (e si dovrebbe ) entrare in FP, un giorno, essere pronti ad abbandonare la nozione di stato globale.
nuovo123456,

Risposte:


689

Le variabili statiche rappresentano lo stato globale. È difficile da ragionare e difficile da testare: se creo una nuova istanza di un oggetto, posso ragionare sul suo nuovo stato all'interno dei test. Se uso un codice che utilizza variabili statiche, potrebbe trovarsi in qualsiasi stato e qualsiasi cosa potrebbe modificarlo.

Potrei andare avanti per un po ', ma il concetto più grande a cui pensare è che più stretto è lo scopo di qualcosa, più facile è ragionare. Siamo bravi a pensare a piccole cose, ma è difficile ragionare sullo stato di un sistema di milioni di linee se non c'è modularità. Questo si applica a tutti i tipi di cose, tra l'altro - non solo alle variabili statiche.


57
Che ultimamente sembra essere un argomento, che sia testabile o meno. È un ragionamento piuttosto imperfetto. L'argomento dovrebbe essere "un buon design", e di solito un buon design è verificabile. Ma non viceversa: "Non posso provarlo per questo deve essere un cattivo design". Non fraintendetemi, sono d'accordo con il tuo post in generale.
M Platvoet,

144
@M Platvoet: direi che, data la scelta tra due design altrimenti ugualmente validi, quello testabile è superiore. Essere testabili certamente non equivale ad essere ben progettato, ma raramente mi sono imbattuto in buoni progetti non verificabili e penso che siano sufficientemente rari da non avere problemi nel rendere la testabilità un indicatore generale che contribuisce a un buon design.
Jon Skeet,

9
@M Platvoet - La testabilità influisce sia sulla manutenibilità che sull'affidabilità, e prenderei in considerazione questi importanti fattori nella qualità del design. Non sono gli unici fattori, sicuramente, ma IMHO il costo di ogni dato codice è una combinazione di cicli macchina, cicli di sviluppo e cicli utente. La testabilità colpisce due di questi tre.
Justin Morgan,

5
@M Platvoet - Anche la testabilità tende a influire sulla riusabilità, dal momento che una classe disaccoppiata è generalmente più facile da riutilizzare.
TrueWill,

13
M Platvoet - Non sono d'accordo con il tuo primo commento qui. Penso che se qualcosa non può essere testato, allora è cattiva progettazione; perché se non riesco a provarlo, non posso sapere che funziona. Compreresti un'auto se il venditore ti dicesse "Il design di questo modello ne impedisce il collaudo, quindi non so se funziona davvero"? La testabilità è così cruciale per il software (così come le automobili), che il design competente RICHIEDE che sia incluso.
Dawood ibn Kareem,

277

Non è molto orientato agli oggetti: una delle ragioni per cui la statica potrebbe essere considerata "malvagia" da alcune persone è che sono contrari al paradigma orientato agli oggetti . In particolare, viola il principio che i dati sono incapsulati negli oggetti (che possono essere estesi, nascondere le informazioni, ecc.). La statica, nel modo in cui descrivi il loro utilizzo, è essenzialmente di usarli come una variabile globale per evitare di affrontare questioni come l'ambito. Tuttavia, le variabili globali sono una delle caratteristiche distintive del paradigma di programmazione procedurale o imperativo, non una caratteristica del codice "buono" orientato agli oggetti. Questo non vuol dire che il paradigma procedurale sia negativo, ma ho l'impressione che il tuo supervisore si aspetti che tu stia scrivendo "buon codice orientato agli oggetti" e che tu voglia davvero scrivere "

Ci sono molti gotchya in Java quando inizi a usare statiche che non sono sempre immediatamente ovvie. Ad esempio, se nella stessa macchina virtuale sono in esecuzione due copie del programma, verranno visualizzati il ​​valore della variabile statica e si confonderanno con lo stato reciproco? O cosa succede quando si estende la classe, è possibile ignorare il membro statico? La tua VM sta esaurendo la memoria perché hai un numero folle di statica e quella memoria non può essere recuperata per altri oggetti istanza necessari?

Durata dell'oggetto: Inoltre, le statistiche hanno una durata che corrisponde all'intero runtime del programma. Ciò significa che, anche dopo aver terminato di utilizzare la classe, la memoria di tutte quelle variabili statiche non può essere spazzatura. Se, ad esempio, invece, hai reso le tue variabili non statiche, e nella tua funzione main () hai creato una singola istanza della tua classe, e poi hai chiesto alla tua classe di eseguire una particolare funzione 10.000 volte, una volta fatte quelle 10.000 chiamate e si eliminano i riferimenti alla singola istanza, è possibile raccogliere e riutilizzare tutte le variabili statiche.

Impedisce un certo riutilizzo: inoltre, i metodi statici non possono essere utilizzati per implementare un'interfaccia, quindi i metodi statici possono impedire l'utilizzo di determinate funzionalità orientate agli oggetti.

Altre opzioni: Se l'efficienza è la tua principale preoccupazione, potrebbero esserci altri modi migliori per risolvere il problema della velocità che considerare solo il vantaggio che l'invocazione è solitamente più veloce della creazione. Valuta se i modificatori transitori o volatili sono necessari ovunque. Per preservare la possibilità di essere integrato, un metodo potrebbe essere contrassegnato come finale anziché statico. I parametri del metodo e altre variabili possono essere contrassegnati come finali per consentire determinate ottimizzazioni del compilatore basate su ipotesi su cosa può cambiare tali variabili. Un oggetto istanza può essere riutilizzato più volte anziché creare una nuova istanza ogni volta. Potrebbero esserci opzioni di ottimizzazione del compilatore che dovrebbero essere attivate per l'app in generale. Forse, il design dovrebbe essere impostato in modo che le 10.000 esecuzioni possano essere multi-thread e sfruttare i core multi-processore. Se la portablità non è

Se per qualche motivo non si desidera più copie di un oggetto, il modello di progettazione singleton, presenta vantaggi rispetto agli oggetti statici, come la sicurezza dei thread (presumendo che il tuo singleton sia ben codificato), consentendo l'inizializzazione lazy, garantendo che l'oggetto sia stato correttamente inizializzato quando viene utilizzato, la sottoclasse, i vantaggi nel test e nel refactoring del codice, per non parlare del fatto che se ad un certo punto ti viene in mente di volere solo un'istanza di un oggetto, è MOLTO più semplice rimuovere il codice per evitare istanze duplicate piuttosto che riformattare tutto il tuo codice variabile statico per usare le variabili di istanza. Prima dovevo farlo, non è divertente, e alla fine devi modificare molte più classi, il che aumenta il rischio di introdurre nuovi bug ... molto meglio per impostare le cose "giuste" la prima volta, anche se sembra che abbia i suoi svantaggi. Per me, la rielaborazione richiesta nel caso in cui decidessi lungo la strada di cui hai bisogno di più copie di qualcosa è probabilmente uno dei motivi più convincenti per utilizzare la statica il più raramente possibile. E quindi non sarei d'accordo con la tua affermazione che la statica riduce le interdipendenze, penso che finirai con un codice che è più accoppiato se hai molte statiche a cui è possibile accedere direttamente, piuttosto che un oggetto che "sa come fare qualcosa "su se stesso.


11
Mi piace la tua risposta, penso che si concentri sui giusti compromessi da considerare intorno alla statica piuttosto che ad alcune delle aringhe rosse come la concorrenza e l'ambito. E +1 per i singleton, una domanda migliore potrebbe essere stata quando usare variabili / metodi statici vs singleton ...
Studgeek,

2
Anche se lo stesso singleton potrebbe essere thread-safe (ad esempio usando synchronizedmetodi), ciò non significa che il codice chiamante sia privo di condizioni di competizione rispetto allo stato singleton.
André Caron,

8
Inoltre, la statica non è contro il paradigma OOP. Molti fanatici di OOP ti diranno che la classe è un oggetto e il metodo statico è un metodo dell'oggetto di classe, piuttosto che le sue istanze. Questo fenomeno è meno presente in Java. Altri linguaggi, come Python, consentono di utilizzare le classi come variabili e di accedere a metodi statici come metodi di quell'oggetto.
André Caron,

4
L'ultima riga del terzo paragrafo dovrebbe leggere tutte le variabili non statiche , se non sbaglio.
Steve

1
Object Lifetime, è un punto molto importante menzionato da @jessica.
Abhidemon,

93

Il male è un termine soggettivo.

Non controlli la statica in termini di creazione e distruzione. Vivono per ordine del programma di caricamento e scaricamento.

Poiché la statica vive in uno spazio, tutti i thread che desiderano usarli devono passare attraverso il controllo di accesso che devi gestire. Ciò significa che i programmi sono più accoppiati e questo cambiamento è più difficile da prevedere e gestire (come dice J Skeet). Ciò comporta problemi di isolamento dell'impatto delle modifiche e influisce quindi sulla gestione dei test.

Questi sono i due problemi principali che ho con loro.


59

No. Gli stati globali non sono di per sé cattivi. Ma dobbiamo vedere il tuo codice per vedere se lo hai usato correttamente. È del tutto possibile che un principiante abusi degli stati globali; proprio come avrebbe abusato di ogni caratteristica della lingua.

Gli stati globali sono necessità assoluta. Non possiamo evitare gli stati globali. Non possiamo evitare il ragionamento sugli stati globali. - Se ci interessa capire la nostra semantica dell'applicazione.

Le persone che cercano di sbarazzarsi degli stati globali per il gusto di farlo, inevitabilmente finiscono con un sistema molto più complesso - e gli stati globali sono ancora lì, abilmente / idioticamente mascherati sotto molti strati di indirette; e dobbiamo ancora ragionare sugli stati globali, dopo aver scartato tutte le indicazioni indirette.

Come le persone di primavera che dichiarano generosamente stati globali in xml e pensano che in qualche modo sia superiore.

@Jon Skeet if I create a new instance of an objectora hai due cose su cui ragionare: lo stato all'interno dell'oggetto e lo stato dell'ambiente che ospita l'oggetto.


10
"Ho due cose su cui ragionare". Non se il mio test dipende solo dallo stato dell'oggetto. Che è più facile, meno lo stato globale che ho.
DJClayworth,

2
L'iniezione di dipendenza non ha nulla a che fare con lo stato globale o la visibilità globale, anche il contenitore stesso non è globale. Rispetto al codice "normale", l'unica cosa in più a cui un oggetto gestito dal contenitore è visibile è al contenitore stesso. Infatti, DI è molto comunemente usato per evitare il Singleton Pattern.
Floegipoky,

31

Esistono 2 problemi principali con le variabili statiche:

  • Sicurezza thread - le risorse statiche non sono per definizione thread-safe
  • Impurità del codice: non si sa quando viene istanziata una variabile statica e se verrà istanziata prima di un'altra variabile statica

Penso che Jon Skeet si riferisse allo stesso commento che hai pubblicato.
RG-3,

13
Non capisco il punto di sicurezza del thread, penso che nulla sia sicuro per il thread a meno che tu non lo faccia. Questo non sembra essere affatto correlato a cose statiche, per favore correggimi se mi manca qualcosa.
Zmaster,

1
@Zmaster - Anche se è vero che la sicurezza dei thread non è un problema esclusivo delle variabili statiche, perché per loro definizione devono essere richiamati da e in contesti diversi, sono più
privi

2
@sternr Capisco cosa intendi, evento se "contesti diversi" non è necessariamente uguale a "thread diversi". Ma è vero che la sicurezza del thread deve spesso essere presa in considerazione con risorse statiche. Dovresti considerare di chiarire la frase.
Zmaster,

Vi sono ad esempio validi usi thread-safe di risorse statiche. Logger finale statico privato LOG = Logger.getLogger (Foo.class); AtomicInteger finale statico privato x = new AtomicInteger (0); Da quanto ho capito, le assegnazioni statiche di risorse come questa sono garantite dal programma di caricamento classi sicuro per i thread. L'istanza di Logger è o meno thread-safe indipendentemente da dove gli si assegna il puntatore. Mantenere lo stato in statica probabilmente non è una buona idea, ma non c'è motivo per cui non dovrebbe essere sicuro per i thread.
Teknopaul,

29

Se stai usando la parola chiave "statica" senza la parola chiave "finale", questo dovrebbe essere un segnale per considerare attentamente il tuo design. Anche la presenza di un 'finale' non è un passaggio libero, poiché un oggetto finale statico mutabile può essere altrettanto pericoloso.

Stimerei da qualche parte circa l'85% delle volte che vedo un 'statico' senza un 'finale', è SBAGLIATO. Spesso, troverò strani accorgimenti per mascherare o nascondere questi problemi.

Si prega di non creare mutabili statici. Soprattutto collezioni. In generale, le raccolte dovrebbero essere inizializzate quando viene inizializzato il loro oggetto contenitore e dovrebbero essere progettate in modo da essere ripristinate o dimenticate quando il loro oggetto contenitore viene dimenticato.

L'uso della statica può creare bug molto sottili che causeranno agli ingegneri giorni di sofferenza. Lo so, perché ho entrambi creato e cacciato questi bug.

Se desideri maggiori dettagli, continua a leggere ...

Perché non usare Statics?

Ci sono molti problemi con la statica, tra cui la scrittura e l'esecuzione di test, così come i bug sottili che non sono immediatamente evidenti.

Il codice che si basa su oggetti statici non può essere facilmente testato dall'unità e la statica non può essere facilmente derisa (di solito).

Se si utilizza la statica, non è possibile scambiare l'implementazione della classe per testare componenti di livello superiore. Ad esempio, immagina un CustomerDAO statico che restituisce gli oggetti Customer caricati dal database. Ora ho un filtro cliente di classe, che deve accedere ad alcuni oggetti cliente. Se CustomerDAO è statico, non posso scrivere un test per CustomerFilter senza prima inizializzare il mio database e popolare informazioni utili.

E la popolazione e l'inizializzazione del database richiedono molto tempo. E nella mia esperienza, il tuo framework di inizializzazione del DB cambierà nel tempo, il che significa che i dati si trasformeranno e i test potrebbero interrompersi. IE, immagina che il Cliente 1 fosse un VIP, ma il framework di inizializzazione del DB è cambiato e ora il Cliente 1 non è più VIP, ma il tuo test è stato codificato per caricare il Cliente 1 ...

Un approccio migliore consiste nell'istanziare un CustomerDAO e trasferirlo nel filtro cliente quando viene costruito. (Un approccio ancora migliore sarebbe utilizzare Spring o un altro framework di Inversion of Control.

Una volta fatto, puoi deridere o stubare rapidamente un DAO alternativo nel tuo Test filtro cliente, permettendoti di avere un maggiore controllo sul test,

Senza il DAO statico, il test sarà più veloce (nessuna inizializzazione db) e più affidabile (perché non fallirà quando cambia il codice di inizializzazione db). Ad esempio, in questo caso assicurarsi che il Cliente 1 sia e sarà sempre un VIP, per quanto riguarda il test.

Esecuzione di test

Le statistiche causano un vero problema quando si eseguono insieme suite di unit test (ad esempio, con il server di integrazione continua). Immagina una mappa statica di oggetti Socket di rete che rimane aperta da un test all'altro. Il primo test potrebbe aprire un socket sulla porta 8080, ma hai dimenticato di cancellare la mappa quando il test viene abbattuto. Ora quando viene avviato un secondo test, è probabile che si blocchi durante il tentativo di creare un nuovo socket per la porta 8080, poiché la porta è ancora occupata. Immagina anche che i riferimenti Socket nella tua raccolta statica non vengano rimossi e (ad eccezione di WeakHashMap) non siano mai idonei per la raccolta dei rifiuti, causando una perdita di memoria.

Questo è un esempio troppo generalizzato, ma in sistemi di grandi dimensioni, questo problema si verifica TUTTO IL TEMPO. Le persone non pensano ai test unitari che avviano e interrompono ripetutamente il loro software nella stessa JVM, ma è un buon test di progettazione del tuo software e se hai aspirazioni verso l'alta disponibilità, è qualcosa di cui devi essere consapevole.

Questi problemi sorgono spesso con oggetti framework, ad esempio accesso ai livelli DB, memorizzazione nella cache, messaggistica e livelli di registrazione. Se stai usando Java EE o alcuni dei migliori framework di razza, probabilmente gestiscono molto di questo per te, ma se come me hai a che fare con un sistema legacy, potresti avere molti framework personalizzati per accedere a questi livelli.

Se la configurazione del sistema che si applica a questi componenti del framework cambia tra i test unitari e il framework unit test non smantella e ricostruisce i componenti, tali cambiamenti non possono avere effetto e quando un test si basa su tali cambiamenti, falliranno .

Anche i componenti non framework sono soggetti a questo problema. Immagina una mappa statica chiamata OpenOrders. Scrivi un test che crea alcuni ordini aperti e verifica che siano tutti nello stato giusto, quindi il test termina. Un altro sviluppatore scrive un secondo test che inserisce gli ordini necessari nella mappa OpenOrders, quindi afferma che il numero di ordini è accurato. Eseguiti singolarmente, entrambi i test passerebbero entrambi, ma quando eseguiti insieme in una suite, falliranno.

Peggio ancora, il fallimento potrebbe essere basato sull'ordine in cui i test sono stati eseguiti.

In questo caso, evitando la statica, si evita il rischio di persistenza dei dati nelle istanze di test, garantendo una migliore affidabilità del test.

Bug sottili

Se si lavora in un ambiente ad alta disponibilità o in qualsiasi punto in cui i thread possono essere avviati e arrestati, la stessa preoccupazione menzionata sopra con le suite di unit test può essere applicata anche quando il codice è in esecuzione in produzione.

Quando si ha a che fare con i thread, anziché utilizzare un oggetto statico per archiviare i dati, è meglio usare un oggetto inizializzato durante la fase di avvio del thread. In questo modo, ogni volta che viene avviato il thread, viene creata una nuova istanza dell'oggetto (con una configurazione potenzialmente nuova) e si evitano che i dati da un'istanza del thread passino all'istanza successiva.

Quando un thread muore, un oggetto statico non viene ripristinato o non viene raccolto. Immagina di avere un thread chiamato "EmailCustomers" e quando si avvia popola una raccolta String statica con un elenco di indirizzi e-mail, quindi inizia a inviare e-mail a ciascuno degli indirizzi. Supponiamo che il thread sia interrotto o annullato in qualche modo, quindi il framework ad alta disponibilità riavvia il thread. Quindi quando il thread si avvia, ricarica l'elenco dei clienti. Ma poiché la raccolta è statica, potrebbe conservare l'elenco di indirizzi e-mail della raccolta precedente. Ora alcuni clienti potrebbero ricevere e-mail duplicate.

A parte: finale statico

L'uso di "static static" è effettivamente l'equivalente Java di un C #define, anche se ci sono differenze di implementazione tecnica. AC / C ++ #define viene sostituito dal codice dal pre-processore, prima della compilazione. Un "finale statico" Java finirà con la memoria residente nello stack. In questo modo, è più simile a una variabile "const statica" in C ++ che a un #define.

Sommario

Spero che questo aiuti a spiegare alcune ragioni di base per cui la statica è problematica. Se si utilizza un moderno framework Java come Java EE o Spring, ecc., È possibile che non si verifichino molte di queste situazioni, ma se si lavora con un ampio corpus di codice legacy, possono diventare molto più frequenti.


15

Dal momento che nessuno * lo ha menzionato: concorrenza. Le variabili statiche possono sorprenderti se hai più thread che leggono e scrivono nella variabile statica. Questo è comune nelle applicazioni Web (ad es. ASP.NET) e può causare alcuni bug piuttosto esasperanti. Ad esempio, se si dispone di una variabile statica che viene aggiornata da una pagina e la pagina viene richiesta da due persone "quasi nello stesso momento", un utente può ottenere il risultato previsto dall'altro utente o, peggio ancora.

la statica riduce le interdipendenze dalle altre parti del codice. Possono agire come titolari di stato perfetti

Spero che tu sia pronto a usare i lucchetti e ad affrontare la contesa.

* In realtà, Preet Sangha lo ha menzionato.


5
Le variabili di istanza non hanno vantaggi di sicurezza dei thread rispetto alla statica, sono tutte variabili non protette. Invece, tutto si riduce al modo in cui proteggi il codice che accede a tali variabili.
Studgeek,

2
Non ho fatto proprio questa affermazione, ma per motivi di discussione: la separazione è una forma di protezione. Gli stati del thread sono separati; lo stato globale no . Una variabile di istanza non necessita di protezione a meno che non sia esplicitamente condivisa tra thread; una variabile statica è sempre condivisa da tutti i thread nel processo.
Justin M. Keyes,

Vorrei che le variabili thread-static fossero più un concetto di prima classe, poiché possono essere molto utili per rendere sicure le informazioni disponibili per una chiamata di subroutine senza dover passare tali informazioni attraverso ogni livello di wrapping. Ad esempio, se un oggetto disponeva di metodi per renderlo nel contesto grafico corrente del thread e esistevano metodi per salvare / ripristinare il contesto grafico corrente, l'utilizzo di questi potrebbe spesso essere più pulito rispetto al dover passare il contesto grafico attraverso ogni chiamata di metodo.
supercat

15

Riassumendo alcuni vantaggi e svantaggi di base dell'uso dei metodi statici in Java:

vantaggi:

  1. Accessibile a livello globale, cioè non legato a nessuna particolare istanza di oggetto.
  2. Un'istanza per JVM.
  3. È possibile accedervi utilizzando il nome della classe (nessun oggetto richiesto).
  4. Contiene un singolo valore applicabile a tutte le istanze.
  5. Carica all'avvio di JVM e muore quando JVM si spegne.
  6. Non modificano lo stato dell'Oggetto.

svantaggi:

  1. I membri statici fanno sempre parte della memoria, indipendentemente dal fatto che siano in uso o meno.
  2. Non puoi controllare la creazione e la distruzione di variabili statiche. Utilmente sono stati creati al caricamento del programma e distrutti quando lo scarico del programma (o quando JVM si spegne).
  3. È possibile rendere sicuro il thread statico utilizzando la sincronizzazione, ma sono necessari ulteriori sforzi.
  4. Se un thread cambia il valore di una variabile statica che può eventualmente interrompere la funzionalità di altri thread.
  5. Devi conoscere "statico" prima di usarlo.
  6. Non è possibile ignorare i metodi statici.
  7. La serializzazione non funziona bene con loro.
  8. Non partecipano al polimorfismo di runtime.
  9. C'è un problema di memoria (in una certa misura, ma non molto immagino) se viene utilizzato un gran numero di variabili / metodi statici. Perché non verranno raccolti in Garbage fino al termine del programma.
  10. Anche i metodi statici sono difficili da testare.

Gli svantaggi 6, 7, 8 e 10 sono gli svantaggi delle lingue / dei framework utilizzati e non gli svantaggi delle variabili statiche in generale. Gli svantaggi 1, 4 e 5 esistono anche per altre soluzioni, come alcuni pattern singleton forniti da alcuni framework. (Non ho votato alla risposta, perché concordo sul resto ed è una bella collezione.)
Peter - Reinstate Monica

13

se dovessi effettuare 10.000 chiamate a una funzione all'interno di una classe, sarei felice di rendere statico il metodo e utilizzare un semplice class.methodCall () su di esso invece di ingombrare la memoria con 10.000 istanze della classe, giusto?

Devi bilanciare la necessità di incapsulare i dati in un oggetto con uno stato, rispetto alla necessità di semplicemente calcolare il risultato di una funzione su alcuni dati.

Inoltre, la statica riduce le interdipendenze dalle altre parti del codice.

Anche l'incapsulamento. In applicazioni di grandi dimensioni, la statica tende a produrre il codice spaghetti e non consente facilmente il refactoring o il test.

Le altre risposte forniscono anche buone ragioni contro un uso eccessivo della statica.


13

Le variabili statiche sono generalmente considerate cattive perché rappresentano lo stato globale e quindi sono molto più difficili da ragionare. In particolare, infrangono i presupposti della programmazione orientata agli oggetti. Nella programmazione orientata agli oggetti, ogni oggetto ha il suo stato, rappresentato da variabili di istanza (non statiche). Le variabili statiche rappresentano lo stato tra istanze che può essere molto più difficile testare l'unità. Ciò è principalmente dovuto al fatto che è più difficile isolare le modifiche alle variabili statiche in un singolo test.

Detto questo, è importante fare una distinzione tra variabili statiche regolari (generalmente considerate cattive) e variabili statiche finali (costanti AKA; non così male).


4
"Le variabili statiche rappresentano lo stato attraverso le classi" ... Penso che intendi "le variabili statiche rappresentano lo stato attraverso le istanze"? +1 per "costanti finali statiche dell'AKA, non così male". Poiché il valore non può cambiare, tutto ciò che dipende da esso in un determinato momento non può cambiare implicitamente il suo comportamento in un secondo momento - il valore è lo stesso.
Jared Updike,

"Le variabili statiche rappresentano lo stato tra istanze" è un modo molto migliore per affermarlo. Ho modificato la mia risposta.
Jack Edmonds,

9

Secondo me non si tratta quasi mai di prestazioni, si tratta di design. Non considero l'uso di metodi statici errato come apposto all'uso di variabili statiche (ma suppongo che tu stia effettivamente parlando di chiamate di metodo).

Si tratta semplicemente di come isolare la logica e dargli un buon posto. A volte ciò giustifica l'utilizzo di metodi statici di cui java.lang.Mathè un buon esempio. Penso che quando nominerai la maggior parte delle tue lezioni XxxUtilo Xxxhelperfaresti meglio a riconsiderare il tuo design.


3
I metodi statici privi di effetti collaterali sono IMO perfettamente perfetti. Ma lo stato mutabile globale lo è raramente e interpreto l'OP come parlare di stato globale.
CodesInChaos,

1
@CodeInChaos è totalmente d'accordo. Trovo che l'OP non sia del tutto chiaro sulla differenza tra metodi statici e var.
M Platvoet,

8

Ho appena sintetizzato alcuni dei punti sollevati nelle risposte. Se trovi qualcosa di sbagliato, sentiti libero di correggerlo.

Ridimensionamento: abbiamo esattamente un'istanza di una variabile statica per JVM. Supponiamo che stiamo sviluppando un sistema di gestione delle biblioteche e abbiamo deciso di mettere il nome del libro come una variabile statica in quanto ne esiste solo una per libro. Ma se il sistema cresce e stiamo usando più JVM, allora non abbiamo modo di capire con quale libro abbiamo a che fare?

Sicurezza thread: Sia la variabile di istanza che la variabile statica devono essere controllate se utilizzate in ambienti multi-thread. Ma nel caso di una variabile di istanza non ha bisogno di protezione a meno che non sia esplicitamente condivisa tra i thread, ma nel caso di una variabile statica è sempre condivisa da tutti i thread nel processo.

Test: sebbene il design testabile non sia uguale a un buon design, ma raramente osserveremo un buon design che non è testabile. Poiché le variabili statiche rappresentano lo stato globale e diventa molto difficile testarle.

Ragionamento sullo stato: se creo una nuova istanza di una classe, possiamo ragionare sullo stato di questa istanza, ma se ha variabili statiche, potrebbe trovarsi in qualsiasi stato. Perché? Perché è possibile che la variabile statica sia stata modificata da qualche istanza diversa poiché la variabile statica è condivisa tra istanze.

Serializzazione: anche la serializzazione non funziona bene con loro.

Creazione e distruzione: la creazione e la distruzione di variabili statiche non possono essere controllate. Di solito vengono creati e distrutti al momento del caricamento e dello scaricamento del programma. Significa che sono dannosi per la gestione della memoria e aggiungono anche il tempo di inizializzazione all'avvio.

E se avessimo davvero bisogno di loro?

Ma a volte potremmo averne una reale necessità. Se sentiamo davvero la necessità di molte variabili statiche condivise nell'applicazione, un'opzione è quella di utilizzare il modello Singleton Design che avrà tutte queste variabili. Oppure possiamo creare alcuni oggetti che avranno queste variabili statiche e che possono essere passati.

Inoltre, se la variabile statica è contrassegnata come finale diventa una costante e il valore assegnato una volta non può essere modificato. Significa che ci salverà da tutti i problemi che dovremo affrontare a causa della sua mutabilità.


7

Mi sembra che tu stia chiedendo delle variabili statiche, ma hai anche indicato metodi statici nei tuoi esempi.

Le variabili statiche non sono malvagie: hanno la sua adozione come variabili globali come costanti nella maggior parte dei casi combinate con il modificatore finale, ma come si dice non abusarne.

Metodi statici aka metodo di utilità. Non è generalmente una cattiva pratica usarli, ma la preoccupazione principale è che potrebbero ostacolare i test.

Come esempio di un grande progetto Java che usa molte statistiche e lo fa nel modo giusto, guarda Play! quadro . C'è anche una discussione al riguardo in SO.

Le variabili / metodi statici combinati con l'importazione statica sono anche ampiamente utilizzati nelle librerie che facilitano la programmazione dichiarativa in Java come: renderlo facile o Hamcrest . Non sarebbe possibile senza molte variabili e metodi statici.

Quindi le variabili statiche (e i metodi) sono buoni ma usali saggiamente!


6

Le variabili statiche, soprattutto, creano problemi con la sicurezza dei dati (in qualsiasi momento modificato, chiunque può cambiare, accesso diretto senza oggetto, ecc.)

Per ulteriori informazioni leggi questo Grazie.


6

Si potrebbe suggerire che nella maggior parte dei casi in cui si utilizza una variabile statica, si desidera utilizzare il modello singleton .

Il problema con gli stati globali è che a volte ciò che ha senso come globale in un contesto più semplice, deve essere un po 'più flessibile in un contesto pratico, ed è qui che il modello singleton diventa utile.


5

Ancora un altro motivo: fragilità.

Se hai una lezione, molte persone si aspettano di essere in grado di crearla e usarla a piacimento.

Puoi documentare che non è il caso o proteggerlo (modello singleton / factory) - ma questo è un lavoro extra e quindi un costo aggiuntivo. Anche allora, in una grande azienda, è probabile che qualcuno ad un certo punto proverà ad usare la tua classe senza prestare piena attenzione a tutti i bei commenti o alla fabbrica.

Se stai usando molte variabili statiche, ciò si interromperà. I bug sono costosi.

Tra un miglioramento delle prestazioni del 0,0001% e la robustezza da cambiare da parte di sviluppatori potenzialmente all'oscuro, in molti casi la robustezza è la scelta giusta.


4

Trovo le variabili statiche più convenienti da usare. E presumo che siano anche efficienti (correggimi se sbaglio) perché se dovessi effettuare 10.000 chiamate a una funzione all'interno di una classe, sarei felice di rendere statico il metodo e utilizzare un semplice class.methodCall () su di esso invece di ingombrare la memoria con 10.000 istanze della classe, giusto?

Vedo cosa ne pensi, ma un semplice schema Singleton farà lo stesso senza dover istanziare 10.000 oggetti.

è possibile utilizzare metodi statici, ma solo per funzioni correlate al dominio dell'oggetto e che non necessitano o utilizzano proprietà interne dell'oggetto.

ex:

public class WaterContainer {
    private int size;
    private int brand;
    ...etc

    public static int convertToGallon(int liters)...

    public static int convertToLiters(int gallon)...

}

Un singleton classico (cioè a cui si accede da Class.Instance) è appena migliore di una variabile statica. È leggermente più testabile, ma è ancora molto peggio dei progetti in cui ti capita di creare un'unica istanza invece di costruire il tuo codice partendo dal presupposto che ce ne sia solo uno.
CodesInChaos,

Non sono sicuro di aver capito il tuo commento! Stavo rispondendo al PO su ciò che ha affermato in corsivo sull'istanza di 10.000 oggetti. Non capisco perché confronti un singleton e una variabile statica? Quello che capisco da quello che hai scritto è che Singleton è un cattivo design ...! Immagino di averti frainteso, dal momento che il Spring Framework rende di default tutti i fagioli Singleton ;-)
Cygnusx1

Un singleton classico (che ha Class.Instance) che porta uno stato mutevole è un cattivo design IMO. In tal caso preferisco fortemente un design in cui ottengo i singoli che devo usare passati come parametro nella classe che li utilizza (in genere con l'aiuto di DI). I singleton classici logicamente immutabili vanno bene IMO.
CodesInChaos,

@ Cygnusx1 Nel caso in cui non fosse chiaro il motivo per cui un singleton di classe (un singleton in cui la classe garantisce che sia una singola copia) non fosse facilmente testabile, accoppia strettamente l'esistenza della classe al ciclo di vita del programma. Per testarlo, è necessario rispettare l'avvio e l'arresto del programma, che spesso ha effetti collaterali non importanti per il test della classe. Se fosse effettivamente come singleton (una copia nel programma, ma non l'applicazione altrimenti), è possibile creare più copie al momento del test senza il programma, verificando che il comportamento all'interno della classe sia come dovrebbe essere per ogni scenario di test.
Edwin Buck,

4

Il problema di "Statics being evil" è più un problema sullo stato globale. Il momento opportuno affinché una variabile sia statica è se non ha mai più di uno stato; Gli strumenti di IE che dovrebbero essere accessibili da tutto il framework e restituire sempre gli stessi risultati per le stesse chiamate di metodo non sono mai "malvagi" come la statica. Per quanto riguarda il tuo commento:

Trovo le variabili statiche più convenienti da usare. E presumo che siano anche efficienti

La statica è la scelta ideale ed efficiente per variabili / classi che non cambiano mai .

Il problema con lo stato globale è l'incoerenza intrinseca che può creare. La documentazione sui test unitari spesso affronta questo problema, poiché ogni volta che esiste uno stato globale a cui possono accedere più di più oggetti non correlati, i test unitari saranno incompleti e non "granulosi". Come menzionato in questo articolo sullo stato globale e sui singoli punti , se gli oggetti A e B non sono correlati (come in uno non viene espressamente fatto riferimento a un altro), A non dovrebbe essere in grado di influenzare lo stato di B.

Ci sono alcune eccezioni al divieto dello stato globale in un buon codice, come l'orologio. Il tempo è globale e, in un certo senso, cambia lo stato degli oggetti senza avere una relazione codificata.


"Il tempo è globale" - ci sono altri modi per modellare il tempo nei sistemi informatici se non quello di avere qualcosa di implicito e globale che cambia da solo. cf. questo sondaggio: "Modeling Time in Computing: A Taxonomy and a Comparative Survey" @ arxiv.org/abs/0807.4132
Jared Updike

4

Il mio $ 0,02 è che molte di queste risposte confondono il problema, piuttosto che dire "le statistiche sono cattive", penso che sia meglio parlare di scoping e istanze.

Quello che direi è che una variabile statica è una "classe" - rappresenta un valore condiviso tra tutte le istanze di quella classe. In genere dovrebbe essere impostato anche in questo modo (protetto o privato per la classe e le sue istanze).

Se hai intenzione di mettere il comportamento a livello di classe attorno ad esso ed esporlo ad altro codice, un singleton potrebbe essere una soluzione migliore per supportare le modifiche in futuro (come suggerito da @Jessica). Questo perché è possibile utilizzare le interfacce a livello di istanza / singleton in modi che non è possibile utilizzare a livello di classe, in particolare l'ereditarietà.

Alcuni pensieri sul perché penso che alcuni degli aspetti di altre risposte non siano fondamentali per la domanda ...

La statica non è "globale". In Java l'ambito viene controllato separatamente da statico / istanza.

La concorrenza non è meno pericolosa per la statica rispetto ai metodi di istanza. È ancora stato che deve essere protetto. Sicuramente potresti avere 1000 istanze con una variabile di istanza ciascuna e una sola variabile statica, ma se il codice che accede non è scritto in un modo sicuro per i thread, sei ancora fregato - potrebbe volerci un po 'più tempo per realizzarlo .

La gestione del ciclo di vita è un argomento interessante, ma penso che sia meno importante. Non vedo perché sia ​​più difficile gestire una coppia di metodi di classe come init () / clear () rispetto alla creazione e alla distruzione di un'istanza singleton. In effetti, alcuni potrebbero dire che un singleton è un po 'più complicato a causa di GC.

PS, in termini di Smalltalk, molti dei suoi dialetti hanno variabili di classe, ma nelle classi Smalltalk sono in realtà istanze di Metaclass, quindi sono realmente variabili nell'istanza di Metaclass. Tuttavia, applicherei la stessa regola empirica. Se vengono utilizzati per lo stato condiviso tra istanze, quindi ok. Se supportano la funzionalità pubblica, dovresti guardare un Singleton. Sospiro, mi manca sicuramente Smalltalk ....


4

Ci sono due domande principali nel tuo post.

Innanzitutto, sulle variabili statiche. Le variabili statiche sono completamente inutili e il suo uso può essere evitato facilmente. Nelle lingue OOP in generale, e in Java in particolare, i parametri di funzione sono passati per riferimento, vale a dire se si passa un oggetto a una funzione, si passa un puntatore all'oggetto, quindi non è necessario definire variabili statiche poiché è possibile passare un puntatore all'oggetto a qualsiasi ambito che necessita di queste informazioni. Anche se questo implica che riempirai la tua memoria di puntatori, ciò non rappresenterà necessariamente una prestazione scadente perché i sistemi di marcatura della memoria effettivi sono ottimizzati per gestire ciò e manterranno in memoria le pagine a cui fanno riferimento i puntatori passati al nuovo scopo; l'uso di variabili statiche può far sì che il sistema carichi la pagina di memoria in cui sono memorizzati quando è necessario accedervi (ciò accadrà se la pagina non è stata acceduta per molto tempo). Una buona pratica è quella di mettere insieme tutto ciò che è statico in alcuni piccoli "clasi di configurazione", questo assicurerà che il sistema metta tutto nella stessa pagina di memoria.

In secondo luogo, sui metodi statici. I metodi statici non sono poi così male, ma possono ridurre rapidamente le prestazioni. Ad esempio, pensa a un metodo che confronta due oggetti di una classe e restituisce un valore che indica quale degli oggetti è più grande (metodo di confronto tipico) questo metodo può essere statico o meno, ma quando lo invochi la forma non statica sarà più efficiente poiché dovrà risolvere solo due riferimenti (uno per ogni oggetto) rivolti ai tre riferimenti che dovranno risolvere la versione statica dello stesso metodo (uno per la classe più due, uno per ogni oggetto). Ma, come ho detto, non è poi così male, se diamo uno sguardo alla classe di matematica, possiamo trovare molte funzioni matematiche definite come metodi statici. Questo è davvero più efficace del mettere tutti questi metodi nella classe che definisce i numeri,

In conclusione: evitare l'uso di variabili statiche e trovare il giusto equilibrio prestazionale quando si affrontano metodi statici o non statici.

PS: scusami per il mio inglese.


4

Non c'è nulla di sbagliato nelle variabili statiche di per sé. È solo la sintassi Java che è rotta. Ogni classe Java in realtà definisce due strutture: un oggetto singleton che incapsula variabili statiche e un'istanza. Definire entrambi nello stesso blocco sorgente è puro male e si traduce in un codice difficile da leggere. Scala ha fatto bene.


3

a) Motivo dei programmi.

Se si dispone di un programma di dimensioni medio-piccole, in cui si accede alla variabile statica Global.foo, la chiamata ad essa normalmente proviene dal nulla: non esiste un percorso e quindi nessuna sequenza temporale, come la variabile arriva nel luogo, dove viene usato. Ora come faccio a sapere chi lo ha impostato sul suo valore reale? Come faccio a sapere cosa succede se lo modifico adesso? Ho grep su tutta la fonte, per raccogliere tutti gli accessi, per sapere cosa sta succedendo.

Se sai come lo usi, perché hai appena scritto il codice, il problema è invisibile, ma se provi a capire il codice straniero, capirai.

b) Ne hai davvero bisogno?

Le variabili statiche spesso impediscono l'esecuzione di più programmi dello stesso tipo nella stessa JVM con valori diversi. Spesso non si prevedono usi, in cui più di un'istanza del programma è utile, ma se si evolve o se è utile per altri, potrebbero verificarsi situazioni in cui vorrebbero avviare più di un'istanza del programma .

Solo un codice più o meno inutile che non verrà utilizzato da molte persone a lungo in modo intensivo potrebbe andare bene con le variabili statiche.


3

tutto (può :) avere il suo scopo, se hai un sacco di thread che devono condividere / memorizzare nella cache i dati e anche tutta la memoria accessibile (quindi non dividere in contesti all'interno di una JVM) la scelta migliore è statica

-> ovviamente puoi forzare solo un'istanza, ma perché?
trovo alcuni dei commenti in questa discussione cattivi, non statici;)


3

Le variabili statiche non sono né buone né cattive. Rappresentano attributi che descrivono l'intera classe e non un'istanza particolare. Se è necessario disporre di un contatore per tutte le istanze di una determinata classe, una variabile statica sarebbe il posto giusto per contenere il valore.

I problemi si presentano quando si tenta di utilizzare le variabili statiche per conservare i valori relativi all'istanza.


2

Tutte le risposte sopra mostrano perché la statica non è buona. Il motivo per cui sono cattivi è perché dà la falsa impressione che tu stia scrivendo codice orientato agli oggetti, mentre in realtà non lo sei. Questo è semplicemente un male.


1
Ma considerare rigidamente il tuo codice per seguire un paradigma standard arbitrario rende effettivamente il codice migliore o ci stiamo lamentando per evitare di scrivere solo codice che funzioni?
Zoey,

Sì, lo rende migliore, perché lo rende più gestibile in futuro, più facile da capire e più esplicito.
Blockhead

1
Perché è male non scrivere il codice OO? e perché Bjarne Stroustrup non è d'accordo con te? per citarne solo uno ...
Marchese di Lorne,

2
Non ho detto che è male non scrivere codice OO. Ho detto che è male pensare di scrivere codice OO, quando tutto ciò che sei è mascherare i globi dietro metodi e proprietà statici. Per favore rileggi quello che ho scritto.
Blockhead

2

Ci sono molte buone risposte qui, aggiungendo ad esso,

Memoria: le variabili statiche sono attive fintanto che il caricatore di classe vive [in generale fino alla morte della macchina virtuale], ma questo è solo nel caso di oggetti / riferimenti in blocco memorizzati come statici.

Modularizzazione: considerare concetti come IOC, dependencyInjection, proxy ecc. Tutti sono completamente contrari a implementazioni strettamente accoppiate / statiche.

Altri svantaggi: sicurezza del thread, testabilità


0

Pensa che se hai un'applicazione con molti utenti e hai definito un modulo statico, ogni utente modificherà anche tutte le altre forme di altri utenti.


0

Penso che un uso eccessivo di variabili globali con parole chiave statiche comporti anche una perdita di memoria in alcuni punti dell'applicazione


0

Dal mio punto di vista, la staticvariabile dovrebbe essere letta solo da dati o variabili creati per convenzione .

Ad esempio abbiamo un'interfaccia utente di alcuni progetti e abbiamo un elenco di paesi, lingue, ruoli utente, ecc. E abbiamo una classe per organizzare questi dati. siamo assolutamente sicuri che l'app non funzionerà senza questo elenco. quindi il primo che facciamo su init app è controllare questo elenco per gli aggiornamenti e ottenere questo elenco da API (se necessario). Quindi siamo d'accordo che questi dati siano "sempre" presenti nell'app. Praticamente sono solo dati di lettura, quindi non abbiamo bisogno di occuparci del suo stato - pensando a questo caso non vogliamo davvero avere molti casi di quei dati - questo caso sembra un candidato perfetto per essere statico .


0

Ho giocato molto con la statica e posso darti una risposta leggermente diversa - o forse un modo leggermente diverso di guardarla?

Quando ho usato la statica in una classe (Membri e metodi entrambi) alla fine ho iniziato a notare che la mia classe è in realtà due classi che condividono la responsabilità - c'è la parte "Statica" che si comporta molto come un singleton e c'è il non -statica (una classe normale). Per quanto ne so, puoi sempre separare completamente queste due classi semplicemente selezionando tutta la statica per una classe e la non statica per l'altra.

Questo accadeva molto quando avevo una raccolta statica all'interno di una classe che conteneva istanze della classe e alcuni metodi statici per gestire la raccolta. Una volta che ci pensi, è ovvio che la tua classe non sta facendo "Solo una cosa", è una raccolta e il fare qualcosa di completamente diverso.

Riflettiamo un po 'il problema: se dividi la tua classe in una classe in cui tutto è statico e un altro che è solo una "Classe normale" e dimentica la "Classe normale", allora la tua domanda diventa una classe puramente statica vs Singleton che è affrontato qui a lungo (e probabilmente una dozzina di altre domande).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.