2010-04-21 9 views
30

Considerare questo scenario. Ho una logica di business che di tanto in tanto sarà necessario scrivere su un log.Quale modello utilizzare per la registrazione? Iniezione delle dipendenze o localizzatore di servizi?

interface ILogger 
{ 
    void Log(string stuff); 
} 

interface IDependency 
{ 
    string GetInfo(); 
} 

class MyBusinessObject 
{ 
    private IDependency _dependency; 

    public MyBusinessObject(IDependency dependency) 
    { 
     _dependency = dependency; 
    } 

    public string DoSomething(string input) 
    { 
     // Process input 
     var info = _dependency.GetInfo(); 
     var intermediateResult = PerformInterestingStuff(input, info); 

     if (intermediateResult== "SomethingWeNeedToLog") 
     { 
      // How do I get to the ILogger-interface? 
     } 

     var result = PerformSomethingElse(intermediateResult); 

     return result; 
    } 
} 

Come si ottiene l'interfaccia di ILogger? Vedo due possibilità principali;

  1. Passarlo utilizzando Iniezione di dipendenza sul costruttore.
  2. Scaricalo tramite un localizzatore di servizi singleton.

Quale metodo preferiresti e perché? O c'è un modello ancora migliore?

Aggiornamento: Nota che non è necessario registrare TUTTE le chiamate di metodo. Voglio solo registrare alcuni (rari) eventi che potrebbero verificarsi o meno nel mio metodo.

+2

una terza opzione sarebbe l'iniezione di proprietà (utilizzando attributi), che potrebbe evitare di ingombrare il costruttore –

risposta

14

Io personalmente faccio una miscela di entrambi.

Qui sono le mie convenzioni:

  • Da un contesto statico - Servizio Ubicazione
  • da un contesto esempio - Dependency Injection

Sento che questo mi dà il giusto equilibrio di testabilità. Trovo un po 'più difficile configurare i test rispetto alle classi che usano Service Location che usare DI, quindi questo è il motivo per cui Service Location finisce per essere l'eccezione piuttosto che la regola. Sono coerente nel suo uso, però, quindi non è difficile ricordare quale tipo di test ho bisogno di scrivere.

Alcuni hanno sollevato la preoccupazione che DI tende a ingombrare i costruttori. Non credo che questo sia un problema, ma se ti senti in questo modo, ci sono un certo numero di alternative che usano DI, ma evitano i parametri del costruttore. Ecco un elenco dei metodi DI di Ninject: http://ninject.codeplex.com/wikipage?title=Injection%20Patterns

Troverete che la maggior parte dei contenitori di Inversion of Control ha le stesse caratteristiche di Ninject. Ho scelto di mostrare Ninject perché hanno i campioni più concisi.

Speriamo che questo sia utile.

Edit: Per essere chiari, io uso l'Unità e Common Service Locator. Ho un'istanza singleton del mio contenitore Unity per DI e la mia implementazione di IServiceLocator è semplicemente un wrapper attorno al container Unity di singleton. In questo modo non devo fare alcun tipo di mappatura due volte o qualcosa del genere.

Inoltre, non ritengo che AOP sia particolarmente utile oltre il tracciamento. Mi piace la registrazione manuale più semplicemente per la sua chiarezza. So che la maggior parte dei framework di registrazione AOP è in grado di entrambi, ma non ho bisogno del primo (pane e burro AOP) la maggior parte del tempo. Questa è solo una preferenza personale, ovviamente.

+1

Grazie, questo si allinea abbastanza bene con i miei sentimenti! – CodingInsomnia

0

DI funzionerebbe bene qui. Un'altra cosa da guardare sarebbe AOP.

0

Non consiglierei nessuno di questi approcci. Meglio usare la programmazione orientata all'aspetto. La registrazione è il "mondo ciao" di AOP.

+0

Beh, sì, ma solo quando è necessario registrare tutte le chiamate (lo faccio già). Ma in questo caso, ho bisogno di registrare cose che accadono solo occasionalmente (registrazione degli eventi piuttosto che registrare le chiamate se capisci cosa intendo). – CodingInsomnia

+0

Se è occasionalmente fatto, direi che DI sarebbe eccessivo. Avrei un membro di classe statico per Logger e avremmo finito con esso. – duffymo

1

Preferirei Singleton Service.

L'iniezione di dipendenza ingombra il costruttore.

Se è possibile utilizzare AOP, questo sarebbe il migliore.

+0

DI! = Costruttori ingombranti. – Will

+0

@Will: puoi spiegare per favore? – M4N

+1

@Will Il downvote sembra un po 'duro. Se si utilizza l'iniezione del costruttore, l'interfaccia di ILogger effettivamente "ingombra" il costruttore con qualcosa che in realtà non è una dipendenza principale. – CodingInsomnia

4

Abbiamo commutato tutti gli attributi di Logging/Tracciamento in PostSharp (AOP framework). Tutto ciò che devi fare per creare la registrazione per un metodo è aggiungere l'attributo ad esso.

Vantaggi:

  • facile utilizzo di AOP
  • Chiaro separazione degli interessi
  • avviene al momento della compilazione ->impatto sulle prestazioni minimo

Controlla this.

+1

Beh, sfortunatamente non posso usare un semplice approccio AOP qui. Non voglio registrare TUTTE le chiamate di metodo, solo alcuni eventi che possono o non possono accadere all'interno del metodo. – CodingInsomnia

3

È possibile derivare un altro tipo, ad es. LoggableBusinessObject che accetta un logger nel suo costruttore. Questo significa che si passa solo nel registratore per gli oggetti che lo utilizzeranno:

public class MyBusinessObject 
{ 
    private IDependency _dependency; 

    public MyBusinessObject(IDependency dependency) 
    { 
     _dependency = dependency; 
    } 

    public virtual string DoSomething(string input) 
    { 
     // Process input 
     var info = _dependency.GetInfo(); 
     var result = PerformInterestingStuff(input, info); 
     return result; 
    } 
} 

public class LoggableBusinessObject : MyBusinessObject 
{ 
    private ILogger _logger; 

    public LoggableBusinessObject(ILogger logger, IDependency dependency) 
     : base(dependency) 
    { 
     _logger = logger; 
    } 

    public override string DoSomething(string input) 
    { 
     string result = base.DoSomething(input); 
     if (result == "SomethingWeNeedToLog") 
     { 
      _logger.Log(result); 
     } 
    } 
} 
+1

Tipo di soluzione interessante, ma sembra un po 'troppo codifica per un compito relativamente semplice. Permette anche di loggare le cose in base al risultato del metodo (mi rendo conto che il mio esempio fa esattamente questo però ..) – CodingInsomnia

+0

+1 uno per una soluzione che mi permette di testare MyBusinessObject senza dover prendere in giro ILogger. – CodingInsomnia

+5

Ma usando questo modello, è possibile accedere solo prima o dopo aver chiamato il metodo della classe base. Non sarai ancora in grado di accedere a metà dei metodi. – M4N

7

Il logger è chiaramente un servizio da cui dipende la logica aziendale e deve quindi essere considerato come una dipendenza nello stesso modo in cui si fa con IDependency. Inietti il ​​logger nel tuo costruttore.

Nota: anche se AOP è menzionato come il modo per iniettare la registrazione non sono d'accordo che sia la soluzione in questo caso. AOP funziona perfettamente per la traccia di esecuzione, ma non sarà mai una soluzione per la registrazione come parte della logica aziendale.

+0

* Il logger è chiaramente un servizio da cui dipende la logica aziendale * - Non sono d'accordo. La tua logica di business non dovrebbe in alcun modo dipendere dalla registrazione e la tua applicazione dovrebbe funzionare altrettanto bene senza di essa. Le tue sessioni di debug, comunque ... –

+0

@ ThorkilHolm-Jacobsen nel caso dell'OP sono: _la logica aziendale che di tanto in tanto sarà richiesta per scrivere su un log_. Sono d'accordo che non è sempre il caso. Ma non sono d'accordo con la tua logica di business non dovrebbe in alcun modo dipendere dal logging_. Ho avuto tali requisiti aziendali in diverse occasioni. –

4

La mia piccola regola:

  • Se è in una libreria di classi, utilizzare uno di iniezione del costruttore o l'iniezione di proprietà con un modello null-oggetto.

  • Se si trova in un'applicazione principale, utilizzare il localizzatore di servizio (o singleton).

Trovo che questo si applica piuttosto bene quando si utilizza log4net. Non vuoi che le librerie di classi raggiungano cose che potrebbero non esserci, ma in un programma applicativo, sai che il logger sarà lì, e le librerie come log4net sono basate pesantemente sul modello di posizione del servizio.

Tendo a considerare la registrazione come qualcosa di sufficientemente statico da non richiedere effettivamente DI. È estremamente improbabile che io possa mai cambiare l'implementazione della registrazione in un'applicazione, soprattutto perché ogni framework di registrazione è incredibilmente flessibile e facile da estendere. È più importante nelle librerie di classi quando la tua biblioteca potrebbe aver bisogno di essere utilizzata da diverse applicazioni che già utilizzano diversi logger.

YMMV, ovviamente. DI è grande ma ciò non significa che tutto debba essere eliminato.

2

Forse questo sarà poco offtopic, ma perché abbiamo bisogno di iniettare logger a tutti, quando possiamo semplicemente digitare al beggining della classe:

Logger logger = LogManager.GetLogger("MyClassName"); 

Logger non cambia durante lo sviluppo e successivamente durante Manutenzione. I moderni logger sono altamente personalizzabili, quindi argomento

e se volessi sostituire il registratore di testi con il database?

mancato.

Non negare l'utilizzo della dipendenza, sono solo curioso della tua mente.

+2

Totalmente d'accordo. Esempio, log4net può essere configurato per accedere praticamente a qualsiasi cosa e puoi anche scrivere il tuo adattatore. E in questo modo puoi sapere quale logger è usato che può essere difficile da capire quando viene iniettato – Jepzen

Problemi correlati