2010-08-13 9 views
9

Ci sono molte domande sul fatto che i singleton siano "cattivi" e quali modelli usare al loro posto. Generalmente sono focalizzati sul modello di progettazione singleton, che implica il recupero dell'istanza singleton da un metodo statico sulla classe. Questa non è una di quelle domande.Sto usando Dependency Injection: quali tipi devo associare come single?

Da quando ho davvero "scoperto" l'iniezione di dipendenza diversi mesi fa, ho guidato la sua adozione nel nostro team, rimuovendo i modelli statici e singleton dal nostro codice nel tempo e utilizzando l'iniezione basata sul costruttore laddove possibile. Abbiamo adottato le convenzioni in modo da non dover continuare ad aggiungere collegamenti espliciti ai nostri moduli DI. Usiamo anche il framework DI per fornire istanze di logger, in modo che possiamo dire automaticamente a ogni logger quale classe è presente senza codice aggiuntivo. Ora che ho un singolo posto in cui posso controllare come sono legati i vari tipi, è davvero facile determinare quale dovrebbe essere il ciclo di vita di una particolare categoria di classi (classi di utilità, archivi, ecc.).

Il mio pensiero iniziale era che probabilmente ci sarebbero stati dei vantaggi rispetto alle classi vincolanti come singleton se mi aspettassi che fossero usati abbastanza spesso. Significa solo che c'è molto meno in attività new, specialmente quando l'oggetto che stai creando ha una grande struttura di dipendenze. Praticamente i soli campi non statici su ognuna di queste classi sono quei valori che vengono iniettati nel costruttore, quindi c'è un sovraccarico di memoria relativamente piccolo per mantenere un'istanza intorno quando non viene utilizzata.

Poi ho letto "Singleton ti amo, ma mi stai portando giù" su www.codingwithoutcomments.com, e mi sono chiesto se avessi avuto l'idea giusta. Dopotutto, Ninject compila le funzioni di creazione dell'oggetto per impostazione predefinita, quindi c'è un piccolo overhead di riflessione coinvolto nella creazione di istanze aggiuntive di questi oggetti. Poiché stiamo mantenendo la business logic fuori dal costruttore, la creazione di nuove istanze è un'operazione davvero leggera. E gli oggetti non hanno un mucchio di campi non statici, quindi non ci sono molti overhead di memoria coinvolti nella creazione di nuove istanze.

Quindi, a questo punto, sto iniziando a pensare che probabilmente non avrà molta importanza in entrambi i casi. Ci sono ulteriori considerazioni che non ho preso in considerazione? Qualcuno ha effettivamente sperimentato un miglioramento significativo delle prestazioni modificando i cicli di vita di determinati tipi di oggetti? Seguire il pattern DI è l'unica cosa veramente importante, o ci sono altri motivi per cui l'uso di una singola istanza di un oggetto può essere intrinsecamente "cattivo?"

risposta

5

Sto usando istanze singleton per memorizzare nella cache costose per creare oggetti (come nibernate session factory) o per oggetti che voglio condividere attraverso l'applicazione.

Se in incertezza preferirei che l'oggetto venga ricreato ogni volta che viene utilizzato e contrassegnarlo come singleton o "per thread"/"per richiesta"/"per qualunque" solo se è davvero necessario.

Singletons dispersione dappertutto per salvare potenzialmente un po 'di novità è l'ottimizzazione prematura e può portare a bug davvero sgradevoli perché non si può essere sicuri se l'oggetto è nuovo o condiviso su più oggetti/istanze.

Anche se l'oggetto non ha stato al momento e sembra salvare per condividere, qualcuno può refactoring in seguito e aggiungere lo stato che può quindi essere involontariamente condivisa tra diversi oggetti/istanze.

+0

+1 per rispondere alla domanda che è stata posta. – StriplingWarrior

0

Non dovrebbe importare molto per l'utilizzo della memoria, ma si otterrà una maggiore modularità, facilità di testabilità con oggetti finti e la bruttezza estetica di dover scrivere le dipendenze in modo esplicito incoraggerà i programmatori a rendere ogni classe più ortogonale e indipendente . È più facile sorvolare sulle dipendenze quando la tua classe chiama globals, che in pratica è un singleton, e può rendere la tua vita più difficile in seguito.

+0

Non penso che tu abbia capito la mia domanda. Io * sto * usando l'iniezione di dipendenza, quindi il codice sembrerà esattamente lo stesso in entrambi i casi. La domanda è se gli oggetti iniettati dovrebbero essere effettivamente la stessa istanza ogni volta, o se ci sia qualche vantaggio nell'avere una nuova istanza consegnata ad ogni classe. – StriplingWarrior

+1

Oh, in quel caso, ci devono essere più informazioni per rispondere correttamente alla tua domanda. Se gli oggetti passati non hanno stato, non c'è differenza, tranne il riferimento all'oggetto extra richiesto per creare una nuova istanza. Se lo fanno, dipende dalle tue esigenze e da ciò che ti aspetti che accada. – gtrak

+0

Gli oggetti non hanno uno stato diverso da altri servizi iniettati, che vengono memorizzati come campi privati. Quindi esiste potenzialmente un grande albero di "nuovi" ogni volta che viene creato un servizio, ma questo è tutto. – StriplingWarrior

1

Non sono sicuro che la gestione della memoria sia un grosso problema, a meno che non si creino MOLTI oggetti. Ho trovato utile utilizzare singleton dal mio contenitore IoC quando ho a che fare con qualcosa come il caching, dove creare l'oggetto caching e creare una connessione con i server di caching è costoso durante la creazione, quindi creiamo l'oggetto di caching una volta e lo riutilizziamo.

Una singola istanza può essere dannosa, tuttavia, poiché può creare un collo di bottiglia nelle prestazioni poiché una singola istanza è in grado di soddisfare più richieste nel caso di servizi.

+0

Puoi approfondire questo secondo paragrafo? Supponendo che le mie classi siano scritte in modo che ogni metodo sia incapsulato, quindi la sicurezza dei thread non è un problema (non devo fare lock o altro), come può rallentare il processo in modo che un'istanza possa servire più richieste? – StriplingWarrior

+1

Se sei sicuro, non c'è nulla di cui preoccuparmi per quanto ne so io. Il mio primo tentativo con IoC qualche tempo fa mi ha colpito con questo problema. Da allora uso singleton per molti servizi e li rendono sicuri. – Wix

0

Come indicato in Gary's answer, l'intero punto di DI è testabilità (si può facilmente prendere in giro tutti i riferimenti a una classe poiché sono tutti elencati nel costruttore), non le prestazioni di runtime.

Si si desidera fare TDD (sviluppo test-drive), giusto?

+0

Come notato nel mio commento sulla risposta di Gary (scritta prima di rispondere), e nel titolo, e ancora il corpo della domanda: * Sto usando l'iniezione di dipendenza *. Non sto chiedendo del "modello" singleton, ma piuttosto se vi sia qualche vantaggio nel legare i miei servizi come singleton o transient nel modulo di configurazione DI. – StriplingWarrior

+0

Capisco. Continuo a pensare che le prestazioni non dovrebbero essere la preoccupazione principale qui. Si consiglia di utilizzare la soluzione che rende il codice più facile da leggere. Tu dici di usare il tuo framework DI per i logger; sembra dare un guadagno di leggibilità (meno confusione nelle classi). Per altri casi, il contrario potrebbe essere vero; potresti voler essere esplicito se il servizio suggerisce l'intenzione della classe/ciclo di vita/categoria. – louisgab

+0

Poiché sto usando DI e vincolo per convenzione, posso aggiungere '.InSingletonScope()' a una riga di codice, e tutte le mie classi di repository saranno magicamente singleton, anche se continuo a usare dependency injection per fornirle classi che ne hanno bisogno. Grazie al framework DI, la leggibilità del codice non verrà influenzata in alcun modo. Sono d'accordo sul fatto che le prestazioni non sono una grande preoccupazione, quindi la mia domanda è se ci sono altre preoccupazioni che dovrei tenere a mente quando prendo questa decisione. – StriplingWarrior

Problemi correlati