quale dovrebbe essere la posizione del logger nell'elenco dei parametri [chiuso]

11

Nel mio codice inserisco un logger in molte delle mie classi tramite la lista dei parametri del costruttore

Ho notato che l'ho messo in modo casuale: a volte è il primo della lista, a volte l'ultimo e talvolta a metà tra

Hai qualche preferenza? La mia intuizione dice che la coerenza è utile in questo caso e la mia preferenza personale è di metterla per prima, in modo che sia più facile essere notato quando manca e più facile saltare quando è lì.

    
posta Ezra 09.03.2015 - 20:47
fonte

7 risposte

30

I logger sono quelli che chiamiamo "preoccupazione trasversale". Si arrendono a tecniche come la programmazione orientata agli aspetti; se hai un modo per decorare le tue classi con un attributo o eseguire tessiture di codice, allora questo è un buon modo per ottenere funzionalità di registrazione mantenendo gli oggetti e gli elenchi di parametri "puri".

L'unica ragione per cui potresti voler passare in un logger è se volessi specificare diverse implementazioni di logging, ma la maggior parte dei framework di logging ha la flessibilità di permetterti di configurarli, ad esempio, per diversi target di registrazione. (file di registro, Gestione eventi di Windows, ecc.)

Per questi motivi, preferisco rendere il logging una parte naturale del sistema, piuttosto che passare un logger in ogni classe per scopi di logging. Quindi quello che faccio in genere è fare riferimento allo spazio dei nomi di registrazione appropriato e utilizzare semplicemente il programma di registrazione nelle mie classi.

Se vuoi ancora passare un logger, la mia preferenza è che tu lo renda l'ultimo parametro nella lista dei parametri (rendilo un parametro facoltativo, se possibile). Averlo essere il primo parametro non ha molto senso; il primo parametro dovrebbe essere il più importante, quello più rilevante per l'operazione della classe.

    
risposta data 09.03.2015 - 21:05
fonte
7

Nelle lingue con sovraccarico di funzioni, direi che più è probabile che un argomento sia opzionale, l'ulteriore diritto dovrebbe essere. Questo crea coerenza quando crei sovraccarichi in cui sono mancanti:

foo(mandatory);
foo(mandatory, optional);
foo(mandatory, optional, evenMoreOptional);

Nei linguaggi funzionali è più utile il contrario: più è probabile che tu scelga un valore predefinito, più a sinistra dovrebbe essere. In questo modo è più facile specializzare la funzione semplicemente applicando argomenti:

addThreeToList = map (+3)

Tuttavia, come menzionato nelle altre risposte, probabilmente non si vuole passare esplicitamente il logger nell'elenco degli argomenti di ogni classe nel sistema.

    
risposta data 09.03.2015 - 21:03
fonte
3

Puoi sicuramente dedicare molto tempo a sovrastimolare questo problema.

Per le lingue con implementazioni di registrazione canoniche, basta istanziare il logger canonico direttamente in ogni classe.

Per le lingue senza un'implementazione canonica, prova a trovare un framework per la facciata del logging e ad attenervisi. slf4j è una buona scelta in Java.

Personalmente preferisco attenermi a una singola implementazione di registrazione concreta e inviare tutto a syslog. Tutti i buoni strumenti di analisi dei registri sono in grado di combinare i registri di sysout da più server di app in un rapporto completo.

Quando una firma di funzione include uno o due servizi di dipendenza oltre ad alcuni argomenti "reali", inserisco le dipendenze per ultime:

int calculateFooBarSum(int foo, int bar, IntegerSummationService svc)

Poiché i miei sistemi tendono ad avere solo cinque o meno tali servizi, io semper assicuri che i servizi siano inclusi nello stesso ordine in tutte le firme di funzioni. L'ordine alfabetico è buono come qualsiasi. (A parte questo, mantenere questo approccio metodologico per la gestione dei mutex ridurrà anche le possibilità di sviluppare deadlock.)

Se ti ritrovi a iniettare più di una dozzina di dipendenze nella tua app, allora il sistema probabilmente deve essere suddiviso in sottosistemi separati (oserei dire microservizi?).

    
risposta data 09.03.2015 - 22:09
fonte
2

I logger sono un po 'un caso speciale perché devono essere disponibili letteralmente ovunque.

Se hai deciso di voler passare un logger nel costruttore di ogni classe, allora dovresti sicuramente impostare una convenzione coerente per come farlo (ad esempio, sempre il primo parametro, sempre passato per riferimento, l'inizializzazione del costruttore la lista inizia sempre con m_logger (theLogger), ecc.). Qualunque cosa che verrà utilizzata nell'intera base di codice trarrà vantaggio dalla coerenza un giorno.

In alternativa, è possibile che ogni classe istanzia il proprio oggetto logger, senza che sia necessario passare nulla. Il logger potrebbe aver bisogno di sapere alcune cose "per magia" perché funzioni, ma codifica un percorso file nella definizione della classe è potenzialmente molto più manutenibile e meno noioso del passare correttamente a centinaia di classi diverse, e probabilmente molto meno male che usare una variabile globale per aggirare il tedio. (Certamente, i logger sono tra i pochissimi casi d'uso legittimi per le variabili globali)

    
risposta data 09.03.2015 - 20:57
fonte
1

Sono d'accordo con quelli che suggeriscono che il logger debba essere acceduto staticamente piuttosto che essere passato in classi. Tuttavia, se esiste un motivo valido per il quale si desidera inoltrarlo (forse diverse istanze vogliono accedere a posizioni diverse o qualcosa del genere), suggerirei di non passarlo usando il costruttore ma piuttosto di effettuare una chiamata separata per farlo, ad es. Class* C = new C(); C->SetLogger(logger); anziché Class* C = new C(logger);

La ragione per preferire questo metodo è che il logger non è una parte essenziale della classe, ma piuttosto una funzionalità iniettata usata per qualche altro scopo. Inserendolo nell'elenco dei costruttori diventa un requisito della classe e implica che fa parte dello stato logico effettivo della classe. È ragionevole aspettarsi, ad esempio (con la maggior parte delle classi anche se non tutte), che se X != Y poi C(X) != C(Y) ma è improbabile che testare la diseguaglianza del logger se si confrontano anche istanze della stessa classe.

    
risposta data 09.03.2015 - 22:53
fonte
0

Vale la pena menzionare, qualcosa su cui non ho visto le altre risposte toccate qui, è che rendendo il logger iniettato tramite proprietà o static, rende difficile (er) testare la classe. Ad esempio, se si effettua l'iniezione del logger tramite la proprietà, ora sarà necessario iniettare quel logger ogni volta che si esegue il test di un metodo che utilizza il registratore. Ciò significa che potrebbe anche impostarlo come dipendenza del costruttore perché la classe lo richiede.

Static si presta allo stesso problema; se il logger non funziona, allora l'intera classe fallisce (se la tua classe usa il logger) - anche se il logger non è necessariamente "parte" della responsabilità della classe - anche se non è così male come l'iniezione di proprietà perché tu almeno sapere che il logger è sempre "lì" in un certo senso.

Solo alcuni spunti di riflessione, soprattutto se impieghi TDD. Secondo me, un logger non dovrebbe davvero essere parte di una parte testabile di una classe (quando collaudi la classe, non dovresti testare anche la tua registrazione).

    
risposta data 10.03.2015 - 11:30
fonte
0

Sono troppo pigro per passare un oggetto logger su ogni istanza di classe. Quindi, nel mio codice, questo tipo di cose si trovano in un campo statico o in una variabile locale del thread in un campo statico. Quest'ultimo è piuttosto interessante e consente di utilizzare un programma di registrazione diverso per ogni thread e consente di aggiungere metodi per attivare e disattivare la registrazione che fanno qualcosa di significativo e atteso in un'applicazione multi-thread.

    
risposta data 10.03.2015 - 11:40
fonte

Leggi altre domande sui tag