La registrazione è successiva a un'implementazione di una violazione SRP?

17

Quando si pensa allo sviluppo del software agile ea tutti i principi (SRP, OCP, ...) mi chiedo come trattare la registrazione.

Il log è accanto a un'implementazione di una violazione SRP?

Direi yes perché anche l'implementazione dovrebbe essere in grado di funzionare senza registrazione. Quindi, come posso implementare la registrazione in un modo migliore? Ho verificato alcuni schemi e sono giunto alla conclusione che il modo migliore per non violare i principi in un modo definito dall'utente, ma per utilizzare qualsiasi modello che è noto per violare un principio è quello di utilizzare un modello decoratore.

Diciamo che abbiamo un sacco di componenti completamente senza violazione SRP e quindi vogliamo aggiungere la registrazione.

  • componente A
  • il componente B utilizza A

Vogliamo logging per A, quindi creiamo un altro componente D decorato con A che implementa un'interfaccia I.

  • interfaccia I
  • componente L (componente di registrazione del sistema)
  • componente A implementa I
  • componente D implementa I, decora / utilizza A, usa L per la registrazione
  • il componente B utilizza un I

Vantaggi: - Posso usare A senza registrazione - testare A significa che non ho bisogno di alcun mock di logging - i test sono più semplici

Svantaggi: - più componenti e più test

So che questa sembra essere un'altra domanda di discussione aperta, ma in realtà voglio sapere se qualcuno utilizza strategie di registrazione migliori di una violazione di decoratore o SRP. Che ne dici del logger statico di Singleton che è come NullLogger predefinito e se si desidera la registrazione di syslog, si modifica l'oggetto di implementazione in fase di runtime?

    
posta Aitch 11.03.2015 - 11:29
fonte

6 risposte

-2

Sì, si tratta di una violazione di SRP poiché la registrazione è una preoccupazione trasversale.

Il modo corretto è quello di delegare la registrazione ad una classe logger (Intercettazione) il cui unico scopo è quello di registrare - rispettando l'SRP.

Vedi questo link per un buon esempio: link

Ecco un breve esempio :

public interface ITenantStore
{
    Tenant GetTenant(string tenant);
    void SaveTenant(Tenant tenant);
}

public class TenantStore : ITenantStore
{
    public Tenant GetTenant(string tenant)
    {....}

    public void SaveTenant(Tenant tenant)
    {....}
} 

public class TenantStoreLogger : ITenantStore
{
    private readonly ILogger _logger; //dep inj
    private readonly ITenantStore _tenantStore;

    public TenantStoreLogger(ITenantStore tenantStore)
    {
        _tenantStore = tenantStore;
    }

    public Tenant GetTenant(string tenant)
    {
        _logger.Log("reading tenant " + tenant.id);
        return _tenantStore.GetTenant(tenant);
    }

    public void SaveTenant(Tenant tenant)
    {
        _tenantStore.SaveTenant(tenant);
        _logger.Log("saving tenant " + tenant.id);
    }
}

I vantaggi includono

  • Puoi testarlo senza registrazione - true test dell'unità
  • puoi facilmente attivare / disattivare la registrazione, anche in fase di runtime
  • puoi sostituire la registrazione per altre forme di registrazione, senza dover modificare il file TenantStore.
risposta data 11.03.2015 - 17:16
fonte
61

Direi che stai prendendo molto sul serio SRP. Se il tuo codice è abbastanza ordinato che la registrazione è l'unica "violazione" di SRP, allora stai facendo meglio del 99% di tutti gli altri programmatori, e dovresti darti una pacca sulla schiena.

Il punto di SRP è di evitare l'orribile codice spaghetti in cui il codice che fa cose diverse è tutto mescolato insieme. Il missaggio di registrazione con codice funzionale non suona alcun campanello d'allarme per me.

    
risposta data 11.03.2015 - 12:49
fonte
15

No, non è una violazione di SRP.

I messaggi che invii al log dovrebbero cambiare per gli stessi motivi del codice circostante.

Che cosa è una violazione di SRP sta utilizzando una libreria specifica per la registrazione direttamente nel codice. Se decidi di cambiare la modalità di registrazione, SRP afferma che non dovrebbe influire sul tuo codice aziendale.

Un qualche tipo di abstract Logger dovrebbe essere accessibile al codice di implementazione, e l'unica cosa che la tua implementazione dovrebbe dire è "Invia questo messaggio al log", senza preoccuparsi di come è stato fatto. Decidere sul modo esatto di registrazione (anche il timestamping) non è responsabilità della tua implementazione.

Quindi la tua implementazione non dovrebbe sapere se il registratore a cui invia i messaggi è un NullLogger .

Detto questo.

Non spazzerei via la registrazione come una preoccupazione trasversale troppo veloce . L'emissione di registri per tracciare eventi specifici che si verificano nel codice di implementazione appartiene al codice di implementazione.

La preoccupazione trasversale, OTOH, è tracciamento dell'esecuzione : la registrazione entra ed esce in ogni singolo metodo. AOP è nella posizione migliore per farlo.

    
risposta data 11.03.2015 - 16:05
fonte
7

Poiché la registrazione è spesso considerata una preoccupazione trasversale, suggerirei di utilizzare AOP per separare la registrazione dall'implementazione.

A seconda della lingua che useresti un intercettore o qualche framework AOP (ad esempio AspectJ in Java) per eseguire questo.

La domanda è se vale davvero la pena. Tieni presente che questa separazione aumenterà la complessità del tuo progetto fornendo al contempo pochissimi vantaggi.

    
risposta data 11.03.2015 - 12:09
fonte
5

Questo suona bene. Stai descrivendo un decoratore di logging abbastanza standard. Hai:

component L (logging component of the system)

Questo ha una responsabilità: registrare le informazioni che gli sono state passate.

component A implements I

Questo ha una responsabilità: fornire un'implementazione dell'interfaccia I (supponendo che I sia correttamente compatibile con SRP, cioè).

Questa è la parte cruciale:

component D implements I, decorates/uses A, uses L for logging

Quando viene indicato in questo modo, sembra complesso, ma guardalo in questo modo: il componente D fa una una cosa: portando A e L insieme.

  • Il componente D non registra; la delega a L
  • Il componente D non implementa I stesso; la delega a A

La responsabilità di solo che il componente D ha è di assicurarsi che L sia notificato quando viene usato A. Le implementazioni di A e L sono entrambe altrove. Questo è completamente compatibile con SRP, oltre ad essere un chiaro esempio di OCP e un uso abbastanza comune dei decoratori.

Un avvertimento importante: quando D usa il componente di registrazione L, dovrebbe farlo in un modo che ti permetta di cambiare come stai loggando. Il modo più semplice per farlo è avere un'interfaccia IL implementata da L. Quindi:

  • Il componente D utilizza un IL per il log; viene fornita un'istanza di L
  • Il componente D utilizza un I per fornire funzionalità; viene fornita un'istanza di A
  • Il componente B utilizza un I; viene fornita un'istanza di D

In questo modo, nulla dipende direttamente da qualcos'altro, rendendo più facile scambiarli. Ciò semplifica l'adattamento ai cambiamenti e facilita la simulazione di parti del sistema in modo da poter eseguire il test dell'unità.

    
risposta data 11.03.2015 - 17:33
fonte
1

Ovviamente è una violazione di SRP perché hai una preoccupazione trasversale. Puoi comunque creare una classe che è responsabile della composizione della registrazione con l'esecuzione di qualsiasi azione.

Esempio:

class Logger {
   ActuallLogger logger;
   public Action ComposeLog(string msg, Action action) {
      return () => {
          logger.debug(msg);
          action();
      };
   }
}
    
risposta data 11.03.2015 - 21:49
fonte