Cuando pienso en el desarrollo ágil de software y todos los principios (SRP, OCP, ...) me pregunto cómo tratar el registro.
¿El registro junto a una implementación es una violación de SRP?
Yo diría yes
que la implementación también debería poder ejecutarse sin iniciar sesión. Entonces, ¿cómo puedo implementar el registro de una mejor manera? Revisé algunos patrones y llegué a la conclusión de que la mejor manera de no violar los principios de una manera definida por el usuario, sino usar cualquier patrón que se sepa que viola un principio es usar un patrón decorador.
Digamos que tenemos un montón de componentes completamente sin violación de SRP y luego queremos agregar el registro.
- componente A
- el componente B usa A
Queremos iniciar sesión para A, por lo que creamos otro componente D decorado con A, ambos implementando una interfaz I.
- interfaz I
- componente L (componente de registro del sistema)
- el componente A implementa I
- el componente D implementa I, decora / usa A, usa L para iniciar sesión
- el componente B usa una I
Ventajas: - Puedo usar A sin iniciar sesión - probar A significa que no necesito ningún simulacro de registro - las pruebas son más simples
Desventaja: - más componentes y más pruebas
Sé que esta parece ser otra pregunta de discusión abierta, pero en realidad quiero saber si alguien usa mejores estrategias de registro que un decorador o una violación de SRP. ¿Qué pasa con el registrador estático singleton que son NullLogger predeterminados y si se desea el registro de syslog, uno cambia el objeto de implementación en tiempo de ejecución?
Respuestas:
Sí, es una violación de SRP ya que el registro es una preocupación transversal.
La forma correcta es delegar el registro a una clase de registrador (Intercepción) cuyo único propósito es iniciar sesión, respetando el SRP.
Consulte este enlace para ver un buen ejemplo: https://msdn.microsoft.com/en-us/library/dn178467%28v=pandp.30%29.aspx
Aquí hay un breve ejemplo :
Beneficios incluidos
fuente
TenantStoreLogger
cambiará cada vez queTenantStore
cambie. No está separando las preocupaciones más que en la solución inicial.Yo diría que te estás tomando SRP demasiado en serio. Si su código es lo suficientemente ordenado como para que el registro sea la única "violación" de SRP, le está yendo mejor que al 99% de todos los demás programadores, y debería felicitarse.
El objetivo de SRP es evitar el espantoso código de espagueti donde el código que hace cosas diferentes está todo mezclado. Mezclar el registro con el código funcional no me suena ninguna alarma.
fuente
Do you mock the logger?
eso es PRECISAMENTE lo que hace. Debe tener unaILogger
interfaz que defina QUÉ hace el registrador. El código bajo prueba se inyecta con unoILogger
que especifique. Para probar, tienesclass TestLogger : ILogger
. Lo mejor de esto es queTestLogger
puede exponer cosas como la última cadena o error registrado. Las pruebas pueden verificar que el código bajo prueba se registre correctamente. Por ejemplo, una prueba podría serUserSignInTimeGetsLogged()
, donde la prueba verificaTestLogger
lo registrado.No, no es una violación de SRP.
Los mensajes que envíe al registro deberían cambiar por los mismos motivos que el código circundante.
Lo que ES una violación de SRP es usar una biblioteca específica para iniciar sesión directamente en el código. Si decide cambiar la forma de iniciar sesión, SRP declara que no debería afectar su código comercial.
Logger
Su código de implementación debe tener acceso a algún tipo de resumen , y lo único que debe decir su implementación es "Enviar este mensaje al registro", sin preocuparse por cómo se hace. Decidir sobre la forma exacta de iniciar sesión (incluso la marca de tiempo) no es responsabilidad de su implementación.Su implementación tampoco debería saber si el registrador al que está enviando mensajes es a
NullLogger
.Eso dicho
No eliminaría la tala como una preocupación transversal demasiado rápido . La emisión de registros para rastrear eventos específicos que ocurren en su código de implementación pertenece al código de implementación.
Lo que es una preocupación transversal, OTOH, es el rastreo de ejecución : el registro entra y sale en todos y cada uno de los métodos. AOP está mejor ubicado para hacer esto.
fuente
Login
interfaz decorada con el mismo registrador.Como el registro a menudo se considera una preocupación transversal, sugeriría usar AOP para separar el registro de la implementación.
Dependiendo del idioma, usaría un interceptor o algún marco AOP (por ejemplo, AspectJ en Java) para realizar esto.
La pregunta es si esto realmente vale la pena. Tenga en cuenta que esta separación aumentará la complejidad de su proyecto mientras proporciona muy pocos beneficios.
fuente
Esto suena bien Estás describiendo un decorador de registro bastante estándar. Tienes:
Esto tiene una responsabilidad: registrar la información que se le pasa.
Esto tiene una responsabilidad: proporcionar una implementación de la interfaz I (suponiendo que cumpla correctamente con SRP, es decir).
Esta es la parte crucial:
Cuando se dice de esa manera, suena complejo, pero mírelo de esta manera: el componente D hace una cosa: unir A y L.
La única responsabilidad que tiene el componente D es asegurarse de que L sea notificado cuando se usa A. Las implementaciones de A y L están ambas en otra parte. Esto es completamente compatible con SRP, además de ser un buen ejemplo de OCP y un uso bastante común de decoradores.
Una advertencia importante: cuando D usa su componente de registro L, debe hacerlo de una manera que le permita cambiar la forma en que está registrando. La forma más sencilla de hacer esto es tener una interfaz IL implementada por L. Luego:
De esa manera, nada depende directamente de nada más, lo que facilita su intercambio. Esto hace que sea fácil adaptarse a los cambios y burlarse de partes del sistema para que pueda realizar pruebas unitarias.
fuente
D implements I
. Gracias por su respuesta.Por supuesto, es una violación de SRP ya que tiene una preocupación transversal. Sin embargo, puede crear una clase responsable de componer el registro con la ejecución de cualquier acción.
ejemplo:
fuente