Non posso semplicemente usare tutti i metodi statici?


64

Qual è la differenza tra i due metodi UpdateSubject di seguito? Sentivo che usare metodi statici è meglio se vuoi solo operare sulle entità. In quali situazioni dovrei scegliere metodi non statici?

public class Subject
{
    public int Id {get; set;}
    public string Name { get; set; }

    public static bool UpdateSubject(Subject subject)
    {
        //Do something and return result
        return true;
    }
    public bool UpdateSubject()
    {
        //Do something on 'this' and return result
        return true;
    }
}

So che riceverò molti calci dalla comunità per questa domanda davvero fastidiosa, ma non potevo smettere di farmi questa domanda.

Questo diventa poco pratico quando si ha a che fare con l'eredità?

Aggiornamento:
sta succedendo nel nostro posto di lavoro ora. Stiamo lavorando su un'applicazione web asp.net di 6 mesi con 5 sviluppatori. Il nostro architetto ha deciso di utilizzare tutti i metodi statici per tutte le API. Il suo ragionamento è che i metodi statici sono leggeri e avvantaggia le applicazioni web mantenendo basso il carico del server.


9
Rich Hickey incoraggerebbe qualcosa di non idiomatico del genere (tutti i metodi statici). Se ti piace uno stile funzionale, potresti anche usare un linguaggio funzionale;) Non tutto nel software è meglio modellato come un oggetto.
Giobbe

13
@Job: non vedo davvero come sia funzionale - è procedurale.
Aaronaught il

2
@Job: questa classe non è immutabile.
Aaronaught il

3
@DonalFellows: Certo che puoi, C # e F # sono entrambi paradigmi misti. Ma resta il fatto, metodi statici! = Programmazione funzionale. FP significa passare / concatenare funzioni, tipi di dati immutabili, evitare effetti collaterali, ecc. Questo è completamente opposto a quello che fa lo snippet di codice nell'OP.
Aaronaught il

4
"Il suo ragionamento è che i metodi statici sono leggeri e avvantaggia le applicazioni web mantenendo il carico del server". Questo è sbagliato. Mantenere basso il carico del server?
mamcx,

Risposte:


87

Vado con i problemi più ovvi di metodi statici con parametri espliciti "questo":

  1. Si perde l'invio virtuale e successivamente il polimorfismo. Non è mai possibile sovrascrivere quel metodo in una classe derivata. Ovviamente puoi dichiarare un metodo new( static) in una classe derivata, ma qualsiasi codice a cui accede deve essere a conoscenza dell'intera gerarchia di classi ed eseguire controlli e casting espliciti, che è esattamente ciò che OO dovrebbe evitare .

  2. Ordinato di un'estensione n. 1, non è possibile sostituire le istanze della classe con un'interfaccia , poiché le interfacce (nella maggior parte delle lingue) non possono dichiarare staticmetodi.

  3. La verbosità inutile. Quale è più leggibile: Subject.Update(subject)o semplicemente subject.Update()?

  4. Verifica argomento. Di nuovo dipende dalla lingua, ma molti compileranno un controllo implicito per assicurarsi che l' thisargomento non sia nullal fine di impedire a un bug di riferimento null di creare condizioni di runtime non sicure (tipo di sovraccarico del buffer). Non usando i metodi di istanza, dovresti aggiungere questo controllo esplicitamente all'inizio di ogni metodo.

  5. È confusionario. Quando un programmatore normale e ragionevole vede un staticmetodo, naturalmente supporrà che non richieda un'istanza valida (a meno che non richieda più istanze, come un metodo di confronto o di uguaglianza, o che si prevede che sia in grado di operare su nullriferimenti) . Vedere metodi statici usati in questo modo ci farà fare un doppio o forse triplo, e dopo la quarta o la quinta volta saremo stressati e arrabbiati e dio ti aiuterà se conosciamo il tuo indirizzo di casa.

  6. È una forma di duplicazione. Ciò che realmente accade quando invochi un metodo di istanza è che il compilatore o il runtime cerca il metodo nella tabella dei metodi del tipo e lo invoca usando thiscome argomento. Fondamentalmente stai implementando nuovamente ciò che il compilatore fa già. Stai violando DRY , ripetendo ripetutamente lo stesso parametro in diversi modi quando non è necessario.

È difficile concepire una buona ragione per sostituire i metodi di istanza con metodi statici. Per favore, no.


5
+1 solo per la tua riga finale. Vorrei che molte più persone pensassero al "metodo di istanza" prima di pensare al "metodo statico".
Stuart Leyland-Cole,

4
+1. Aggiungerei che i metodi statici rendono difficile scrivere test, dato che nella maggior parte dei casi non puoi deriderli: una volta che una parte del tuo codice dipende da un metodo statico, non puoi sostituirlo con un no- nei test. Un primo esempio nel mondo .NET sarebbe classi piace File, FileInfoe DirectoryInfo(e, in realtà, ci sono le biblioteche che li nascondono dietro le interfacce che possono poi essere iniettati come necessario e deriso in test).
sm

Penso che i tuoi punti sul polimorfismo / ereditarietà siano controversi. non si dovrebbe usare l'ereditarietà per il riutilizzo del codice, quindi è irrilevante. anche la parte ereditaria è dubbia. nella maggior parte dei linguaggi moderni, le firme dei metodi sono polimorfiche in sé. se segui un approccio funzionale inizi a passare da un metodo all'altro e componi metodi complessi da metodi semplici intercambiabili in base alla loro interfaccia. non ci sono praticamente svantaggi per i metodi statici che non sono compensati semplicemente progettando pensando a loro, come con OOP.
Sara

@sm semplicemente non è vero. se hai una forte dipendenza da QUALCOSA, statica o no, che non può essere in un unit test, allora non è verificabile, punto. statico non causa questo. un metodo statico può essere iniettato allo stesso modo di una classe che rappresenta una connessione db. stai assumendo che "statico" significa "statico più dipendenze nascoste che toccano lo stato globale e le risorse esterne". non è giusto.
Sara

@kai prova a iniettare un metodo statico che fa Y invece di X, qualunque sia X e Y. Non è necessario prendere dipendenze da qualsiasi cosa esterna da avvitare. Forse sei solo interessato a testare alcuni aspetti che non richiedono un lungo calcolo X da eseguire. Come iniettare il tuo metodo statico elaborato senza creare un wrapper? Il passaggio di funzioni non è supportato da tutte le lingue. I metodi statici hanno i loro casi d'uso e li uso quando ha senso farlo . Questo è, come sempre accade, un caso di "solo perché non puoi necessariamente significare che dovresti".
sm

13

Hai praticamente descritto esattamente come esegui OOP in C, in cui sono disponibili solo metodi statici e "this" è un puntatore a struttura. E sì, puoi eseguire il polimorfismo del tempo di esecuzione in C specificando un puntatore a funzione durante la costruzione da memorizzare come elemento della struttura. I metodi di istanza disponibili in altre lingue sono solo zucchero sintattico che essenzialmente fa esattamente la stessa cosa sotto il cofano. Alcuni linguaggi, come Python, sono a metà strada, dove il parametro "self" è esplicitamente elencato, ma una sintassi speciale per chiamarlo gestisce l'ereditarietà e il polimorfismo per te.


FWIW, con C puoi fare l'invio virtuale in questo modo: obj->type->mthd(obj, ...);ed è sostanzialmente simile a quello che succede con l'invio virtuale in altre lingue (ma dietro le quinte).
Donal Fellows,

@Karl Puoi fornire qualche tipo di riferimento scritto per iniziare a scavare più a fondo?
Jorge Lavín,

Potresti voler considerare GObject come una buona implementazione di riferimento.
Karl Bielefeldt,

10

Il problema con il metodo statico arriva nel momento in cui è necessaria una sottoclasse.

I metodi statici non possono essere sovrascritti nelle sottoclassi, quindi le nuove classi non possono fornire nuove implementazioni dei metodi, rendendoli meno utili.


2
Questo non è necessariamente un problema. Alcuni linguaggi forniscono altri meccanismi (metodi non virtuali in C ++ e C #) per realizzare intenzionalmente la stessa cosa - C # fornisce persino metodi "sigillati" per estendere ulteriormente questa idea! Ovviamente, non lo faresti solo per diamine, ma è una buona tecnica di cui essere a conoscenza ...
Shog9

@Steve, che non sta sostituendo, in quanto è ancora possibile chiamare entrambi i metodi direttamente.

@Steve, in Java i metodi statici non possono essere sovrascritti.

@ Thorbjørn: in primo luogo, la domanda non è taggata con Java o con qualsiasi altro linguaggio OOP specifico. Ancora più importante, non ho detto che potresti ignorare una funzione membro statica. Stavo cercando di usare un'analogia per fare un punto. Come ho chiaramente fallito, i commenti cancellati.
Steve314,

2
In un certo senso, si tratta ancora di "nuove implementazioni di metodi". Solo non nel senso standard di OOP. Mi sento un po 'come se stessi dicendo "neanche la tua formulazione, nur-nur-na-nur-nur" ;-)
Steve314

7

Se si dichiara un metodo static, non è necessario creare un'istanza di un oggetto dalla classe (utilizzando la newparola chiave) per eseguire il metodo. Tuttavia, non è possibile fare riferimento a nessuna variabile membro a meno che non siano anche statiche, nel qual caso tali variabili membro appartengono alla classe, non a un oggetto istanziato specifico della classe.

Detto in altri termini, la staticparola chiave fa sì che una dichiarazione faccia parte della definizione della classe, quindi diventa un singolo punto di riferimento in tutte le istanze della classe (oggetti istanziati dalla classe).

Ne consegue, quindi, che se si desidera più copie in esecuzione della propria classe e non si desidera che lo stato (variabili membro) sia condiviso tra tutte le copie in esecuzione (ovvero ogni oggetto detenga il proprio stato univoco), si non è possibile dichiarare quelle variabili membro statiche.

In generale, è necessario utilizzare staticclassi e metodi solo quando si creano metodi di utilità che accettano uno o più parametri e restituiscono un oggetto di qualche tipo, senza effetti collaterali (ovvero modifiche alle variabili di stato nella classe)


1
Sottile che l'OP capisca cosa staticsignifica, sta chiedendo perché non dichiarare tutto come statico.
Ed S.

1
Egli si passa in un'istanza di Subject- ma come un parametro esplicito, al posto del implicita thisparametro.
Steve314,

Beh, sì ... ma non vedo il tuo punto suppongo.
Ed S.

@Ed - solo che puoi fare praticamente qualsiasi cosa attraverso un parametro esplicito che potresti attraverso un thisparametro implicito . Ci sono differenze nelle aspettative, ma al di fuori dell'eredità, non c'è vera differenza in ciò che puoi fare. Ad esempio, è possibile accedere a quei membri non statici tramite il parametro esplicito.
Steve314,

Stavo operando supponendo che non si potesse ottenere membri privati ​​dell'argomento in C # anche attraverso un metodo statico dichiarato nella stessa classe (come è possibile in C ++). Non so perché l'ho pensato, ma mi sbagliavo.
Ed S.

3

Il motivo per cui le persone sostengono che i "metodi statici mostrano una cattiva progettazione" non sono necessariamente dovuti ai metodi, sono in realtà dati statici, impliciti o espliciti. Per implicito intendo QUALSIASI stato nel programma che non è contenuto nei valori o nei parametri di ritorno. La modifica dello stato in una funzione statica è un ritorno alla programmazione procedurale. Tuttavia, se la funzione non modifica lo stato o delega la modifica dello stato alle funzioni degli oggetti componenti, in realtà è più un paradigma funzionale che procedurale.

Se non cambi stato, i metodi e le classi statici possono avere molti vantaggi. Rende molto più semplice garantire che un metodo sia puro e quindi privo di effetti collaterali e che tutti gli errori siano completamente riproducibili. Assicura inoltre che metodi diversi in più applicazioni che utilizzano una libreria condivisa possano essere certi che otterranno gli stessi risultati con lo stesso input, rendendo molto più facile sia il rilevamento degli errori che il test delle unità.

In definitiva, non vi è alcuna differenza nella pratica tra oggetti sovradimensionati comunemente visti come "StateManager" e dati statici o globali. Il vantaggio dei metodi statici usati correttamente è che possono indicare l'intenzione dell'autore di non modificare lo stato in revisori successivi.


1
La tua affermazione "... è un ritorno alla programmazione procedurale ..." mi fa pensare che lo consideri così male, immagino che faccia parte di OO in un certo senso.
NoChance,

making ... unit testing much easier.No non lo farà. Rende impossibile il test unitario di qualsiasi codice che chiama metodi statici , poiché non è possibile isolarlo dal metodo statico. Una chiamata a un metodo statico diventa una dipendenza hardcoded a quella classe.
StuperUser

2

A seconda della lingua e di ciò che stai cercando di fare, la risposta potrebbe essere che non c'è molta differenza, ma la staticversione ha un po 'più di confusione.

Come la tua sintassi di esempio suggerisce Java o C #, penso che Thorbjørn Ravn Andersen abbia ragione nel sottolineare il problema prevalente (con associazione tardiva). Con un metodo statico, non esiste un parametro "speciale" su cui basare l'associazione tardiva, quindi non è possibile eseguire l'associazione tardiva.

In effetti, un metodo statico è solo una funzione in un modulo, quel modulo con lo stesso nome della classe - non è affatto un metodo OOP.


2

Fondamentalmente stai sconfiggendo l'intero punto degli oggetti quando lo fai. C'è una buona ragione per cui siamo passati dal codice procedurale al codice orientato agli oggetti.

Avrei mai dichiarare un metodo statico che ha preso il tipo di classe come parametro. Questo rende il codice più complesso senza alcun guadagno.


3
Potresti farlo se passassi objecta un staticmetodo e ne restituissi uno nuovo object. I programmatori funzionali lo fanno sempre.
Robert Harvey,

domanda: consideri la Mathclasse statica "complessa per nessun guadagno" o la preferiresti se tutti i tipi di numero avessero metodi virtuali che definiscono valore assoluto, negazione, aggiunta, quadratura, radici arbitrarie e così via? Io non la penso così. gli oggetti sono puliti quando è necessario memorizzare lo stato mutabile nascosto in cui è necessario applicare alcuni invarianti. unendo tutti i metodi concreti che operano su un tipo nel tipo stesso, QUELLO è qualcosa che porta a un codice complesso e non mantenibile. Sono d'accordo con Robert, potresti voler vedere come funzionano i programmatori funzionali, può essere un vero colpo d'occhio!
Sara

@kai Come ho già detto, praticamente. La classe di matematica è un'eccezione ragionevole, anche se collegarla ai tipi numerici non sarebbe una cattiva idea.
Loren Pechtel,

2

Ci sono molte grandi risposte qui, e sono molto più perspicaci e ben informati di qualsiasi cosa io possa mai trovare una risposta, ma sento che c'è qualcosa che non viene affrontato:

"Il nostro architetto ha deciso di utilizzare tutti i metodi statici per tutte le API. Il suo ragionamento è che i metodi statici sono leggeri e avvantaggia le applicazioni Web mantenendo basso il carico del server ."

(l'enfasi audace è mia)

Il mio 2c su questa parte della domanda è questo:

Teoricamente, ciò che viene detto è vero: chiamare un metodo statico ha solo il sovraccarico di invocare effettivamente quel metodo. La chiamata a un metodo non statico (istanza) ha l'ulteriore sovraccarico di prima istanziare l'oggetto e, a un certo punto, distruggere l'istanza (manualmente o tramite una qualche forma di garbage collection automatica, a seconda della piattaforma utilizzata).

Gioca un po 'di avvocato del diavolo su questo: possiamo andare ancora oltre e dire cose come:

  • questo può diventare davvero brutto se viene creata un'istanza per ogni invocazione del metodo (istanza) (invece di lasciarla statica e chiamarla così)

  • a seconda della complessità del costruttore, del tipo di gerarchia, di altri membri dell'istanza e di altri fattori sconosciuti, l'overhead aggiuntivo della chiamata del metodo non statico può variare e diventare molto grande

In verità, IMHO, i punti precedenti (e l'approccio generale di "usiamo la statica perché sono più veloci") sono argomenti / valutazioni di paglia:

  • se il codice è valido e, più specifico di questo argomento, se le istanze vengono create solo quando necessario (e distrutte in modo ottimale), non si ottiene alcun sovraccarico aggiuntivo dall'uso dei metodi di assicurazione quando appropriato (perché se si creare solo gli oggetti necessari, quelli sarebbero stati creati anche se quel metodo fosse stato dichiarato statico in alcuni casi = stesso overhead di instanciazione)

  • abusando della dichiarazione dei metodi statici in questo modo, è possibile nascondere alcuni problemi con il codice in merito alla modalità di creazione delle istanze (poiché il metodo è statico, un codice di instanciazione errato può passare inosservato fino a dopo, e non è mai un bene).


2

Questa è una domanda molto interessante che tende ad avere risposte molto diverse a seconda della comunità che stai chiedendo. Altre risposte sembrano essere C # o java bias: un linguaggio di programmazione che è (principalmente) puro oggetto orientato e ha una sorta di visione idiomatica di quale sia l'orientamento dell'oggetto.

In altre comunità, come C ++, l'orientamento agli oggetti viene interpretato con maggiore liberalità. Alcuni ben sanno che gli esperti hanno studiato l'argomento e, in realtà, concludono che le funzioni libere migliorano l'incapsulamento (l'enfasi è mia):

Abbiamo ora visto che un modo ragionevole per valutare la quantità di incapsulamento in una classe è contare il numero di funzioni che potrebbero essere interrotte se l'implementazione della classe cambia. Stando così le cose, diventa chiaro che una classe con n funzioni membro è più incapsulata di una classe con n + 1 funzioni membro. E questa osservazione è ciò che giustifica il mio argomento per preferire le funzioni non amiche non membro alle funzioni membro

Scott Meyers

Per chi non ha familiarità con la terminologia C ++:

  • funzione membro = metodo
  • funzione non membro = metodo statico
  • non amico = usa solo API pubbliche
  • non membro funzione non amico = metodo statico che utilizza solo API pubbliche

Ora, per rispondere alla tua domanda:

Non posso semplicemente usare tutti i metodi statici?

No, non dovresti sempre usare metodi statici.

Ma dovresti usarli ogni volta che devi solo usare l' API pubblica di una classe.


-3

in Java ...

I metodi statici non vengono sovrascritti ma sono nascosti e quindi sarebbe una grande differenza (problema?) In caso di estensione della classe.

D'altra parte ho notato che i programmatori Java hanno la tendenza a pensare che TUTTO DEVE essere oggetto, senza davvero capire il perché, e non sono d'accordo.

Statico dovrebbe essere usato per il codice specifico della classe. L'elettricità statica non funziona bene con l'ereditarietà.

alcuni buoni esempi di utilizzo di metodi statici sono funzioni indipendenti senza effetti collaterali.

vedi anche la parola chiave sincronizzata ...


2
Come si nasconde questo metodo statico e dove emergerebbero questa differenza e problema estendendo la classe? Come si sincronizza la sincronizzazione con questo? Dove si incontrano i problemi di staticità ed eredità? Puoi fornire alcuni metodi buoni e cattivi delle librerie Java di base e spiegare perché ognuno è il caso?
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.