cuál debería ser la posición del registrador en la lista de parámetros [cerrado]


12

En mi código, inyecto un registrador en muchas de mis clases a través de la lista de parámetros de su constructor

Me di cuenta de que lo puse al azar: a veces es el primero en la lista, a veces el último y a veces entre

¿Tienes alguna preferencia? Mi intuición dice que la consistencia es útil en este caso y mi preferencia personal es ponerla en primer lugar para que sea más fácil ser notado cuando falta y más fácil omitirlo cuando esté allí.

Respuestas:


31

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.


11
+! mantener el registrador fuera de los parms del constructor prefiero un registrador estático, así que sé que todos están usando lo mismo
Steven A. Lowe

1
@ StevenA.Lowe Sin embargo, tenga en cuenta que el uso de registradores estáticos puede ocasionar otros problemas en el futuro si no tiene cuidado (por ejemplo, fiasco de orden de inicialización estática en C ++). Estoy de acuerdo en que tener el registrador como una entidad accesible a nivel mundial tiene sus encantos, pero se debe evaluar cuidadosamente si ese diseño se ajusta a la arquitectura general y cómo lo hace.
ComicSansMS

@ComicSansMS: por supuesto, más problemas de subprocesos, etc. Solo una preferencia personal - "lo más simple posible, pero no más simple";)
Steven A. Lowe

Tener un registrador estático puede ser un problema. Hace que la inyección de dependencia sea más difícil (a menos que se refiera a un registrador singleton instanciado por su contenedor DI), y si alguna vez desea cambiar su arquitectura, puede ser doloroso. En este momento, por ejemplo, estoy usando funciones de Azure que pasan un registrador como parámetro para la ejecución de cada función, así que lamento pasar mi registrador a través del constructor.
Slothario

@Slothario: Esa es la belleza de un registrador estático. No se requiere inyección de ningún tipo.
Robert Harvey

7

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:

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

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:

addThreeToList = map (+3)

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.


3

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?).


Me parece incómodo usar la inyección de propiedades para llamar a CalculateFooBarSum.
un phu

2

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)


1

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 != Yasí fuera , C(X) != C(Y)pero es poco probable que pruebe la desigualdad del registrador si compara también instancias de la misma clase.


1
Por supuesto, esto tiene el inconveniente de que el registrador no está disponible para el constructor.
Ben Voigt

1
Realmente me gusta esta respuesta. Hace que el registro sea una preocupación secundaria de la clase que debe preocuparse por separado, solo por usarla. Lo más probable es que si está agregando el registrador al constructor de una gran cantidad de clases, probablemente esté utilizando la inyección de dependencia. No puedo hablar para todos los idiomas, pero sé que en C #, algunas implementaciones de DI también inyectarán directamente a las propiedades (getter / setter).
jpmc26

@BenVoigt: Eso es cierto, y puede ser una razón asesina para no hacerlo de esta manera, pero, por lo general, puede hacer el registro que de otro modo haría en el constructor en respuesta a la configuración de un registrador.
Jack Aidley

0

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).


1
hmmm ... por lo que desea que su clase realice el registro (el registro debe estar en la especificación) pero no desea probar usando un registrador. es posible? Creo que tu punto es un no ir. Claramente, si sus herramientas de prueba están rotas, no se puede probar: diseñar de una manera que no dependa de una herramienta de prueba es un poco de ingeniería excesiva en mi humilde opinión
Hogan

1
Mi punto es que si uso un constructor y llamo a un método en una clase y todavía falla porque no configuré una propiedad, entonces el diseñador de la clase ha entendido mal el concepto detrás de un constructor. Si la clase requiere un registrador, debe inyectarse en el constructor, para eso está el constructor.
Dan Pantry

umm .. no, no realmente. Si considera que el sistema de registro es parte del "marco", entonces no tiene sentido como parte del constructor. Pero esto se ha establecido claramente en otras respuestas.
Hogan

1
Estoy argumentando en contra de la inyección de propiedad. No estoy abogando necesariamente por el uso de inyectarlo en el constructor. Solo digo que, en mi opinión, eso es preferible a la inyección de propiedades.
Dan Pantry

"¿Es posible?" Además, sí, el tejido IL es algo que existe y se mencionó en la respuesta principal ... mono-project.com/docs/tools+libraries/libraries/Mono.Cecil
Dan Pantry

0

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.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.