Un tipo singleton può sostituire i metodi e le classi statici? [duplicare]

15

In C # i metodi statici hanno a lungo servito uno scopo che ci consente di chiamarli senza creare classi di istanziazione. Solo nell'ultimo anno siamo diventati più consapevoli dei problemi legati all'utilizzo di metodi e classi statici.

  • Non possono utilizzare le interfacce
  • Non possono utilizzare l'ereditarietà
  • Sono difficili da testare perché non puoi fare mock e stub

C'è un modo migliore? Ovviamente dobbiamo essere in grado di accedere ai metodi di libreria senza classi istanziate per tutto il tempo altrimenti il nostro codice diventerebbe piuttosto disordinato

Una possibile soluzione è usare una nuova parola chiave per un vecchio concetto: the singleton . Singleton sono istanze globali di una classe, dal momento che sono casi in cui possiamo usarle come faremmo con le normali classi. Per rendere il loro uso piacevole e pratico avremmo bisogno di un po 'di zucchero sintattico comunque

Supponete che la classe Math sia di tipo singleton invece di una classe effettiva. La classe effettiva contenente tutti i metodi predefiniti per il singleton Math è DefaultMath, che implementa l'interfaccia IMath. Il singleton verrebbe dichiarato come

singleton Math : IMath
{
   public Math
   {
      this = new DefaultMath();
   }
}

Se volessimo sostituire la nostra classe per tutte le operazioni matematiche, potremmo creare una nuova classe

MyMath che eredita DefaultMath , oppure potremmo ereditare dall'interfaccia IMath e creare una classe completamente nuova. Per rendere la nostra classe la classe Math attiva, dovresti fare un semplice incarico

Math =  new MyMath();

e voilá! la prossima volta che chiameremo Math.Floor chiamerà il tuo metodo. Nota che per un normale singleton dovremmo scrivere qualcosa come Math.Instance.Floor ma il compilatore elimina la necessità della proprietà Instance

Un'altra idea sarebbe quella di essere in grado di definire un singleton come Lazy in modo che vengano istanziati solo quando vengono chiamati per la prima volta, come

lazy singleton Math : IMath

Cosa ne pensi, sarebbe stata una soluzione migliore per metodi e classi statici? C'è qualche problema con questo approccio?

Addendum

Alcuni punti sono stati sollevati, che uno dei principali vantaggi dei metodi statici è quello di avere metodi "stateless" e quindi di effetti collaterali liberi in una certa misura dal punto di vista della concorrenza. Sono pienamente d'accordo che questo è un punto valido. Tuttavia stiamo mescolando qui due problemi e problemi diversi: uno sta rendendo alcuni metodi invocabili e accessibili a livello globale senza dover creare un'istanza in modo esplicito. L'altro sta avendo metodi che sono apolidi.

Il secondo problema potrebbe essere risolto con un metodo che ha qualcosa come una parola chiave senza stato che in modo simile alla statica ha impedito loro di chiamare questo o forse fare ancora di più per applicare nessun effetto collaterale. Con singoletti piuttosto che classi statiche e qualcosa come classi e metodi apolidi penso che avresti i seguenti pro e contro

Pro

  • Metodi "statici" in altre classi e le classi quadro potrebbero facilmente essere progettato per essere sovrascrivibile
  • Le classi sarebbero più facili da testare
  • Utilizzare le istanze anziché le classi statiche significa che i modelli di progettazione funzionano meglio (cose come le fabbriche)
  • Nessuna limitazione di ereditarietà e polimorfismo in contrasto con statico

Contro

  • Forse prestazioni leggermente peggiori?
  • I programmatori cattivi faranno tutto in singleton per renderli accessibili a livello globale invece di usare dependency injection, forse dovresti essere in grado di accedere solo ai metodi singleton e non a proprietà / campi per evitare variabili globali:)

Forse Math era un cattivo esempio, ma immagina che se i metodi di stringa .Net fossero inefficienti, potresti facilmente sostituirli con il tuo usando questo metodo. Oppure alcune classi di terze parti hanno un metodo singleton che vuoi leggermente modificare, puoi ereditare e modificare quel metodo

    
posta Homde 10.03.2011 - 14:06
fonte

3 risposte

15

Non c'è differenza nei pericoli cambiando i metodi statici in metodi di istanza su un singleton. Sono effettivamente uguali.

A volte hai semplicemente delle funzioni. Una funzione in termini matematici, non contiene nessuno stato. Sono semplicemente i calcoli da applicare a un input per determinare l'output. Ad esempio, Math.Sqrt non contiene nessuno stato. Esegue un calcolo sul valore fornito dall'utente e restituisce una risposta derivata da tale valore. In C, C ++, Ruby, Perl e altri linguaggi che supportano funzioni semplici non è necessario legare quella funzione a nessuna classe, statica o meno. In Java e C # tutte le funzioni devono essere legate ad alcune classi - che è tecnicamente un hack.

È importante notare che le vere funzioni sono perfette per rimanere un metodo statico. Ad esempio, tutto nella classe Math è definito correttamente. Non mi piace fare qualcosa di stupido come Math.Instance.Sqrt(x) solo per soddisfare uno standard "no static method".

La verità è che la tua proprietà Instance in questo caso sarebbe il tipo di metodo statico che causa la maggior parte dei problemi. La ragione? Non è una funzione: mantiene lo stato e modifica il suo comportamento in base allo stato interno. Al primo accesso, determina che l'istanza globale non esiste e quindi la crea. Negli accessi successivi riutilizza quell'istanza. Negli ambienti multi-threading questo può creare una condizione di competizione che potenzialmente può creare più istanze della classe Math . Alla fine si vincerà e rimarrà memorizzato, mentre gli altri riceveranno la raccolta dei rifiuti. Con la classe Math la condizione di gara sarebbe semplicemente un'inefficienza conveniente, ma se il singleton dovesse mantenere lo stato da tutti i suoi client, allora sarebbe un problema. Se aggiungi lock s all'accesso di singleton, hai introdotto un collo di bottiglia di prestazioni importanti per le applicazioni multi-thread che peggiora solo con il numero di thread. Ciò è dovuto soprattutto al fatto che il blocco è necessario solo fino alla creazione dell'istanza.

Lascia che le tue funzioni siano il più vicino possibile alle funzioni e non dovrai mai preoccuparti di questi problemi di singleton multi-thread. Nelle lingue che supportano le funzioni, implementale come funzioni. Nelle lingue che richiedono un metodo statico, rendili un metodo statico. Singletons presenta una serie di problemi che vengono alla luce solo quando si approfondisce il progetto.

    
risposta data 10.03.2011 - 15:43
fonte
5

Se la domanda è "Che cosa usiamo per sostituire le classi statiche?"

Singleton non sono la risposta.

Iniezione di dipendenza è.

Detto questo, sto usando i singleton per sostituire le classi statiche in una base di codice legacy, in questo caso è molto più fattibile per apportare questa modifica che aggiungere DI, e ci permette di ottenere il vecchio codice sotto test molto più velocemente. Vedi questo domanda di overflow dello stack per ulteriori informazioni su questo

Aggiorna
D1: Come utilizzeresti l'iniezione di dipendenza a livello di framework per cose come le funzioni Math e File?

A livello di struttura? Dato che è possibile aggiungere il supporto linguistico per Singleton , aggiungiamo invece il supporto nel framework per DI in spazi dei nomi come Math, IO, ecc.
La configurazione della macchina fornirà sezioni che - per impostazione predefinita - aggiungono i sapori di framework di questi, ganci che possiamo rimuovere nei file app.config e aggiungere nel nostro (o altri per i test)

Q2: Inoltre, se si potesse valutare quale classe userebbe un singleton per classi diverse, non sarebbe un'iniezione di dipendenza di fatto?

Suoni che mi confondono, quindi invece del tuo singleton che rappresenta un oggetto singolo , ora rappresenta più oggetti e ne serve di diversi a seconda del contesto di come è chiamato? Non sarebbe un a) renderlo un multipleton eb) essere un incubo spaventoso per cercare di tenere traccia di ciò che un determinato metodo di Math farà, a seconda di come / dove lo si chiama.

    
risposta data 10.03.2011 - 14:19
fonte
0

La questione di interfacciare e ereditare classi statiche mi ha tenuto impegnato per un po ', anche se a livello teorico più che pratico.

Ci sono anche un paio di domande su SO che riguardano l'argomento.

Finalmente ho elaborato una soluzione che consiste essenzialmente in

  • la separazione tra la parte "classe reale" e la parte "classe statica" in 2 classi
  • un attributo di classe che assegna a una classe la sua classe pseudo-statica
  • una classe helper (statica) che recupera e crea un'istanza della classe pseudo-statica per una classe

Per maggiori dettagli, leggi questa mini serie sul mio blog .

    
risposta data 19.01.2012 - 21:11
fonte

Leggi altre domande sui tag