Los madereros son lo que llamamos una "preocupación transversal". Ceden ante técnicas como la Programación Orientada a Aspectos; Si tiene una manera de decorar sus clases con un atributo o realizar algún tejido de código, entonces esa es una buena manera de obtener capacidades de registro mientras mantiene sus objetos y listas de parámetros "puros".
La única razón por la que puede querer pasar un registrador es si desea especificar diferentes implementaciones de registro, pero la mayoría de los marcos de registro tienen la flexibilidad para permitirle configurarlos, por ejemplo, para diferentes objetivos de registro. (archivo de registro, Administrador de eventos de Windows, etc.)
Por estas razones, prefiero hacer que el registro sea una parte natural del sistema, en lugar de pasar un registrador a cada clase para fines de registro. Entonces, lo que generalmente hago es hacer referencia al espacio de nombres de registro apropiado y simplemente usar el registrador en mis clases.
Si aún desea pasar un registrador, mi preferencia es que lo convierta en el último parámetro de la lista de parámetros (si es posible, hágalo un parámetro opcional). Tenerlo como primer parámetro no tiene mucho sentido; El primer parámetro debe ser el más importante, el más relevante para la operación de la clase.
En lenguajes con sobrecarga de funciones, argumentaría que cuanto más probable es que un argumento sea opcional, más a la derecha debería ser. Esto crea coherencia cuando crea sobrecargas donde faltan:
En los lenguajes funcionales, lo contrario es más útil: cuanto más probable sea que elija algún valor predeterminado, más a la izquierda debería ser. Esto facilita la especialización de la función simplemente aplicándole argumentos:
Sin embargo, como se menciona en las otras respuestas, probablemente no desee pasar explícitamente el registrador en la lista de argumentos de cada clase en el sistema.
fuente
Definitivamente, puede dedicar mucho tiempo a la ingeniería excesiva de este problema.
Para los idiomas con implementaciones de registro canónico, simplemente cree una instancia del registrador canónico directamente en cada clase.
Para los idiomas sin una implementación canónica, intente encontrar un marco de fachada de registro y apegarse a él. slf4j es una buena opción en Java.
Personalmente prefiero apegarme a una única implementación de registro concreta y enviar todo a syslog. Todas las buenas herramientas de análisis de registros son capaces de combinar registros de sysout de múltiples servidores de aplicaciones en un informe completo.
Cuando una firma de función incluye uno o dos servicios de dependencia, así como algunos argumentos "reales", coloco las dependencias al final:
int calculateFooBarSum(int foo, int bar, IntegerSummationService svc)
Como mis sistemas tienden a tener solo cinco o menos servicios, siempre me aseguro de que los servicios se incluyan en el mismo orden en todas las firmas de funciones. El orden alfabético es tan bueno como cualquiera. (Aparte: mantener este enfoque metodológico para el manejo de mutex también reducirá sus posibilidades de desarrollar puntos muertos).
Si se encuentra inyectando más de una docena de dependencias en su aplicación, entonces el sistema probablemente deba dividirse en subsistemas separados (¿me atrevo a decir microservicios?).
fuente
Los registradores son un caso especial porque tienen que estar disponibles literalmente en todas partes.
Si ha decidido que desea pasar un registrador al constructor de cada clase, entonces definitivamente debe establecer una convención coherente para cómo hacerlo (por ejemplo, siempre el primer parámetro, siempre pasado por referencia, la lista de inicialización del constructor siempre comienza con m_logger (theLogger), etc. Cualquier cosa que se vaya a utilizar en toda su base de código se beneficiará de la coherencia algún día.
Alternativamente, puede hacer que cada clase cree una instancia de su propio objeto de registro, sin necesidad de pasar nada. Es posible que el registrador necesite saber algunas cosas "por arte de magia" para que funcione, pero codificar una ruta de archivo en la definición de clase es potencialmente un mucho más fácil de mantener y menos tedioso que pasarlo correctamente a cientos de clases diferentes, y podría decirse que es mucho menos malvado que usar una variable global para evitar dicho tedio. (Es cierto que los registradores se encuentran entre los pocos casos de uso legítimos para variables globales)
fuente
Estoy de acuerdo con aquellos que sugieren que se debe acceder estáticamente al registrador en lugar de pasarlo a clases. Sin embargo, si hay una razón de peso que desea pasar en (tal vez diferentes instancias desean iniciar sesión a diferentes ubicaciones o algo así), entonces te sugeriría que no lo pasa con el constructor, sino más bien hacer una llamada por separado a este, por ejemplo,
Class* C = new C(); C->SetLogger(logger);
en vez queClass* C = new C(logger);
La razón para preferir este método es que el registrador no es esencialmente una parte de la clase, sino más bien una característica inyectada utilizada para algún otro propósito. Colocarlo en la lista de constructores lo convierte en un requisito de la clase e implica que es parte del estado lógico real de la clase. Es razonable esperar, por ejemplo (con la mayoría de las clases, aunque no todas), que si
X != Y
así fuera ,C(X) != C(Y)
pero es poco probable que pruebe la desigualdad del registrador si compara también instancias de la misma clase.fuente
Vale la pena mencionar, algo que no he visto tocar en las otras respuestas aquí, es que al inyectar el registrador a través de propiedades o estático, hace que sea difícil (er) probar la clase en la unidad. Por ejemplo, si inyecta su registrador mediante una propiedad, ahora tendrá que inyectarlo cada vez que pruebe un método que utilice el registrador. Esto significa que también podría tenerlo configurado como una dependencia de constructor porque la clase lo requiere.
La estática se presta al mismo problema; si el registrador no funciona, entonces toda su clase falla (si su clase usa el registrador), aunque el registrador no es necesariamente 'parte' de la responsabilidad de la clase, aunque no es tan malo como la inyección de propiedad porque al menos sepa que el registrador siempre está "allí" en cierto sentido.
Solo algo de reflexión, especialmente si emplea TDD. En mi opinión, un registrador realmente no debería ser parte de una parte comprobable de una clase (cuando prueba la clase, no debería probar también su registro).
fuente
Soy demasiado vago para pasar un objeto logger a cada instancia de clase. Entonces, en mi código, este tipo de cosas se sientan en un campo estático o una variable local de hilo en un campo estático. Este último es genial y le permite usar un registrador diferente para cada subproceso y le permite agregar métodos para activar y desactivar el inicio de sesión que hacen algo significativo y esperado en una aplicación de subprocesos múltiples.
fuente