¿Son las llamadas "preocupaciones transversales" una excusa válida para romper SOLID / DI / IoC?

43

A mis colegas les gusta decir "el registro / almacenamiento en caché / etc. es una preocupación transversal" y luego continúan usando el singleton correspondiente en todas partes. Sin embargo, aman IoC y DI.

¿Es realmente una excusa válida para romper el principio SOLI D ?

Guarida
fuente
27
He descubierto que los principios SÓLIDOS son principalmente útiles solo para aquellos que ya tienen la experiencia necesaria para seguirlos sin saberlo de todos modos.
svidgen
1
He encontrado que el término "preocupación transversal" es un término bastante específico (relacionado con aspectos) y no necesariamente es sinónimo de singleton. El registro puede ser una preocupación transversal en el sentido de que a veces desea registrar un mensaje genérico en varios lugares de la misma manera (por ejemplo, registrar cada llamada a un método de servicio con quién realizó la llamada o algún otro tipo de registro de auditoría). Sin embargo, diría que la tala (en el sentido general) no es una preocupación transversal solo porque se usa en todas partes. Creo que esto es más una "preocupación omnipresente", aunque ese no es realmente un término estándar.
Paso
44
@svidgen los principios SOLID también son útiles cuando aquellos que no los conocen revisan su código y le preguntan por qué lo hizo de esa manera. Es bueno poder señalar un principio y decir "por eso"
candied_orange
1
¿Tiene algún ejemplo de por qué cree que el registro es un caso especial? Es un canal bastante simplista para redirigir algunos datos, generalmente parte de cualquier marco X que esté utilizando.
ksiimson

Respuestas:

42

No.

SOLID existe como pautas para dar cuenta del cambio inevitable. ¿ Realmente nunca va a cambiar su biblioteca de registro, o destino, o filtrado, o formateo, o ...? ¿ Realmente no va a cambiar su biblioteca de almacenamiento en caché, o su objetivo, o estrategia, o alcance, o ...?

Por supuesto que lo eres. Por lo menos , querrás burlarte de estas cosas de una manera sensata para aislarlas para la prueba. Y si va a querer aislarlos para la prueba, es probable que se encuentre con una razón comercial en la que desea aislarlos por razones de la vida real.

Y luego obtendrá el argumento de que el propio registrador manejará el cambio. "¡Oh, si el objetivo / filtrado / formato / estrategia cambia, entonces solo cambiaremos la configuración!" Eso es basura. Ahora no solo tiene un Objeto de Dios que maneja todas estas cosas, sino que está escribiendo su código en XML (o similar) donde no obtiene análisis estático, no obtiene errores de tiempo de compilación y no ' Realmente obtengo pruebas unitarias efectivas.

¿Hay casos para violar las pautas de SOLID? Absolutamente. A veces las cosas no cambiarán (sin requerir una reescritura completa de todos modos). A veces, una ligera violación de LSP es la solución más limpia. A veces, hacer una interfaz aislada no proporciona ningún valor.

Pero el registro y el almacenamiento en caché (y otras preocupaciones transversales ubicuas) no son esos casos. Por lo general, son excelentes ejemplos de los problemas de acoplamiento y diseño que obtienes cuando ignoras las pautas.

Telastyn
fuente
22
¿Qué alternativa tienes entonces? ¿Inyectando un ILogger en cada clase que escribes? ¿Escribir un decorador para cada clase que necesita un registrador?
Robert Harvey
16
Sí. Si algo es una dependencia, hágala una dependencia . Y si eso significa demasiadas dependencias, o si está pasando un registrador en algún lugar al que no debería ir, bien , ahora puede arreglar su diseño.
Telastyn
20
El verdadero problema que tengo con la IoC dogmática es que casi todo el código moderno depende de algo que no se inyecta ni se extrae. Si se escribe C #, por ejemplo, no estás yendo a menudo abstracta Stringo Int32incluso Listfuera de su módulo. Solo hay una medida en que es razonable y sensato planificar un cambio. Y, más allá de los tipos "básicos" en su mayoría obvios, discernir lo que probablemente cambiarás es realmente solo una cuestión de experiencia y juicio.
svidgen
66
@svidgen - Espero que no hayas leído mi respuesta en defensa de la IoC dogmática. Y solo porque no abstraigamos estos tipos principales (y otras cosas donde sea prudente) no significa que esté bien tener una Lista / Cadena / Int / etc global.
Telastyn
38

Este es el punto central del término "preocupación transversal": significa algo que no encaja perfectamente en el principio SÓLIDO.

Aquí es donde el idealismo se encuentra con la realidad.

Las personas semi-nuevas a SOLID y transversales a menudo se encuentran con este desafío mental. Está bien, no te asustes. Esfuércese por poner todo en términos de SOLID, pero hay algunos lugares como el registro y el almacenamiento en caché donde SOLID simplemente no tiene sentido. El corte transversal es el hermano de SOLID, van de la mano.

Klom Dark
fuente
99
Una respuesta razonada, práctica, no dogmática.
user1936
11
¿Puede decirnos por qué los cinco principios SÓLIDOS se rompen por preocupaciones transversales? Tengo problemas para entender eso.
Doc Brown
44
Sí. Potencialmente podría pensar en una buena razón para "romper" cada principio individualmente; ¡Pero ni una sola razón para romper los 5!
svidgen
1
Sería ideal para mí no tener que golpearme la cabeza contra la pared cada vez que necesito burlarme de un registrador / caché singleton para una prueba unitaria. Imagine cómo sería probar una aplicación web sin tener que hacerlo HttpContextBase(que se introdujo exactamente por este motivo). Sé con certeza que mi realidad sería realmente amarga sin esta clase.
devnull
2
@devnull tal vez ese es el problema, en lugar de las preocupaciones transversales en sí mismas ...
Boris the Spider
25

Para iniciar sesión, creo que es. El registro es generalizado y generalmente no está relacionado con la funcionalidad del servicio. Es común y bien entendido utilizar el marco de registro de patrones únicos. Si no lo hace, está creando e inyectando registradores en todas partes y no quiere eso.

Un problema de lo anterior es que alguien dirá 'pero ¿cómo puedo probar el registro?' . Mis pensamientos son que normalmente no pruebo el registro, más allá de afirmar que realmente puedo leer los archivos de registro y comprenderlos. Cuando he visto el registro probado, generalmente es porque alguien necesita afirmar que una clase realmente ha hecho algo y están usando los mensajes de registro para obtener esa retroalimentación. Preferiría registrar algún oyente / observador en esa clase y afirmar en mis pruebas que eso se llama. Luego puede colocar su registro de eventos dentro de ese observador.

Sin embargo, creo que el almacenamiento en caché es un escenario completamente diferente.

Brian Agnew
fuente
3
Después de haber incursionado un poco en FP, ahora tengo un gran interés en usar la composición de funciones (tal vez monádica) para incrustar cosas como el registro, el manejo de errores, etc. en sus casos de uso sin poner nada de eso en la lógica comercial central ( ver fsharpforfunandprofit.com/rop )
sara
55
El registro no debe ser generalizado. Si es así, entonces estás registrando demasiado y solo estás creando ruido cuando realmente necesitas mirar esos registros. Al inyectar la dependencia del registrador, se ve obligado a pensar si realmente necesita o no registrar algo.
RubberDuck
12
@RubberDuck: dígame que "el registro no debe ser generalizado" cuando recibo un informe de error del campo y la única capacidad de depuración que tengo para descubrir qué sucedió es ese archivo de registro que no quería que fuera generalizado. Lección aprendida, la tala debe ser "generalizada", muy generalizada.
Dunk
15
@RubberDuck: con el software del servidor, el registro es la única forma de sobrevivir. Es la única forma de descubrir un error que ocurrió hace 12 horas en lugar de hacerlo ahora en su propia computadora portátil. No te preocupes por el ruido. Utilice el software de gestión de registros para que pueda consultar su registro (idealmente, también debe configurar alarmas que le enviarán un correo electrónico en los registros de errores)
slebetman
66
Nuestro personal de soporte me llamó y me dijo "el cliente estaba haciendo clic en algunas páginas y apareció un error". Les pregunto qué dijo el error, no lo saben. Pregunto qué hizo clic específicamente el cliente, no lo saben. Pregunto si pueden reproducirse, no pueden. Estoy de acuerdo en que el registro se puede lograr principalmente con registradores bien implementados en algunos puntos clave, pero los pequeños mensajes extraños aquí y allá también son invaluables. El miedo a iniciar sesión conduce a un software de menor calidad. Si termina con demasiados datos, pode de nuevo. No optimices prematuramente.
Paso
12

Mis 2 centavos ...

Si y no.

Nunca deberías realmente violar los principios que adoptas; pero, sus principios siempre deben ser matizados y adoptados en servicio a un objetivo superior. Entonces, con una comprensión adecuadamente condicionada, algunas violaciones aparentes pueden no ser violaciones reales del "espíritu" o del "conjunto de principios en su conjunto".

Los principios SÓLIDOS en particular, además de requerir muchos matices, están en última instancia subordinados al objetivo de "entregar software funcional y mantenible". Por lo tanto, adherirse a cualquier principio SOLID en particular es contraproducente y contradictorio cuando hacerlo entra en conflicto con los objetivos de SOLID. Y aquí, a menudo me doy cuenta de que entregar la capacidad de mantenimiento triunfa .

Entonces, ¿qué pasa con la D en SÓLIDO ? Bueno, contribuye a una mayor capacidad de mantenimiento al hacer que su módulo reutilizable sea relativamente independiente de su contexto. Y podemos definir el "módulo reutilizable" como "una colección de código que planea usar en otro contexto distinto". Y eso se aplica a funciones individuales, clases, conjuntos de clases y programas.

Y sí, cambiar las implementaciones del registrador probablemente coloque su módulo en "otro contexto distinto".

Entonces, permítanme ofrecerles mis dos grandes advertencias :

Primero: Dibujar las líneas alrededor de los bloques de código que constituyen "un módulo reutilizable" es una cuestión de juicio profesional. Y su juicio se limita necesariamente a su experiencia.

Si actualmente no planea usar un módulo en otro contexto, probablemente esté bien que dependa indefensamente de él. La advertencia a la advertencia: sus planes probablemente estén equivocados, pero eso también está bien. Cuanto más tiempo escriba módulo tras módulo, más intuitivo y preciso será su sentido de si "necesitaré esto de nuevo algún día". Pero, probablemente nunca podrá decir retrospectivamente: "He modularizado y desacoplado todo en la mayor medida posible, pero sin excesos ".

Si se siente culpable por sus errores de juicio, confiese y continúe ...

En segundo lugar: invertir el control no equivale a inyectar dependencias .

Esto es especialmente cierto cuando comienzas a inyectar dependencias hasta la saciedad . La inyección de dependencia es una táctica útil para la estrategia global de IoC. Pero, diría que la DI es de menor eficacia que otras tácticas, como el uso de interfaces y adaptadores, puntos únicos de exposición al contexto desde dentro del módulo.

Y centrémonos realmente en esto por un segundo. Porque, incluso si inyecta una Logger náusea ad , necesita escribir código en la Loggerinterfaz. No podría comenzar a usar una nueva Loggerde un proveedor diferente que tome parámetros en un orden diferente. Esa capacidad proviene de la codificación, dentro del módulo, contra una interfaz que existe dentro del módulo y que tiene un solo submódulo (Adaptador) para administrar la dependencia.

Y si está codificando contra un Adaptador, si el Loggerinyectado en ese Adaptador o descubierto por el Adaptador generalmente es bastante insignificante para el objetivo general de mantenimiento. Y lo más importante, si tiene un adaptador de nivel de módulo, probablemente sea absurdo inyectarlo en cualquier cosa. Está escrito para el módulo.

tl; dr : deja de preocuparte por los principios sin tener en cuenta por qué los estás utilizando. Y, más prácticamente, solo construya un Adapterpara cada módulo. Use su criterio al decidir dónde dibuja los límites del "módulo". Desde cada módulo, siga adelante y consulte directamente el Adapter. Y claro, inyecte el registrador real en el Adapter, pero no en cada pequeña cosa que pueda necesitarlo.

svidgen
fuente
44
Hombre ... hubiera dado una respuesta más corta, pero no tenía tiempo.
svidgen
2
+1 por usar un adaptador. Debe aislar las dependencias de componentes de terceros para poder reemplazarlas siempre que sea posible. El registro es un objetivo fácil para esto: existen muchas implementaciones de registro y en su mayoría tienen API similares, por lo que una implementación de adaptador simple puede permitirle cambiarlas muy fácilmente. Y para las personas que dicen que nunca cambiarán su proveedor de registro: he tenido que hacer esto y me causó un gran dolor porque no estaba usando un adaptador.
Jules
"Los principios SÓLIDOS en particular, además de requerir muchos matices, están en última instancia subordinados al objetivo de" entregar software funcional y mantenible "". Esta es una de las mejores declaraciones que he visto. Como codificador levemente TOC, he tenido mi parte de luchas con el idealismo frente a la productividad.
DVK
Cada vez que veo un +1 o un comentario sobre una respuesta anterior, leo mi respuesta nuevamente y descubro, nuevamente, que soy un escritor terriblemente desorganizado y poco claro ... Sin embargo, agradezco el comentario, @DVK.
svidgen
Los adaptadores son salvavidas, no cuestan mucho y hacen su vida mucho más fácil, especialmente cuando desea aplicar SOLID. La prueba es muy fácil con ellos.
Alan
8

La idea de que el registro siempre debe implementarse como un singleton es una de esas mentiras que se ha dicho con tanta frecuencia que ha ganado fuerza.

Durante el tiempo que los sistemas operativos modernos se han ocupado, se ha reconocido que es posible que desee iniciar sesión en varios lugares dependiendo de la naturaleza de la salida .

Los diseñadores de sistemas deberían cuestionarse constantemente la eficacia de las soluciones pasadas antes de incluirlas ciegamente en otras nuevas. Si no están llevando a cabo tal diligencia, entonces no están haciendo su trabajo.

Robbie Dee
fuente
2
¿Cuál es su solución propuesta, entonces? ¿Inyecta un ILogger en cada clase que escribes? ¿Qué pasa con el uso de un patrón Decorador?
Robert Harvey
44
Realmente no estoy respondiendo mi pregunta ahora, ¿verdad? Expresé los patrones simplemente como ejemplos ... No tiene que ser esos si tienes algo mejor en mente.
Robert Harvey
8
Realmente no entiendo esta respuesta, en términos de propuestas concretas
Brian Agnew
3
@RobbieDee: ¿Entonces está diciendo que el registro debe implementarse de la manera más complicada e inconveniente por la "posibilidad improbable" de que podamos querer iniciar sesión en varios lugares? Incluso si ocurriera un cambio como ese, ¿realmente cree que agregar esa funcionalidad a la instancia de Logger existente será más difícil que todo el esfuerzo para pasar ese Logger entre clases y cambiar las interfaces cuando decida que desea un Logger o no? ¿Las docenas de proyectos donde ese registro de múltiples lugares nunca ocurrirá?
Dunk
2
Re: "es posible que desee iniciar sesión en varios lugares dependiendo de la naturaleza de la salida": Claro, pero la mejor manera de manejar eso es en el marco de registro , en lugar de intentar inyectar múltiples dependencias de registro separadas. (Los marcos de registro comunes ya lo admiten.)
ruakh
4

El registro genuino es un caso especial.

@Telastyn escribe:

¿Realmente nunca va a cambiar su biblioteca de registro, o destino, o filtrado, o formateo, o ...?

Si anticipa que podría necesitar cambiar su biblioteca de registro, entonces debería estar usando una fachada; es decir, SLF4J si estás en el mundo de Java.

En cuanto al resto, una biblioteca de registro decente se encarga de cambiar dónde va el registro, qué eventos se filtran, cómo se formatean los eventos de registro utilizando archivos de configuración del registrador y (si es necesario) clases de complementos personalizados. Hay una serie de alternativas disponibles.

En resumen, estos son problemas resueltos ... para iniciar sesión ... y, por lo tanto, no hay necesidad de usar la inyección de dependencia para resolverlos.

El único caso en el que DI podría ser beneficioso (sobre los enfoques de registro estándar) es si desea someter el registro de su aplicación a pruebas unitarias. Sin embargo, sospecho que la mayoría de los desarrolladores dirían que el registro no es parte de la funcionalidad de una clase, y no es algo que deba probarse.

@Telastyn escribe:

Y luego obtendrá el argumento de que el propio registrador manejará el cambio. "¡Oh, si el objetivo / filtrado / formato / estrategia cambia, entonces solo cambiaremos la configuración!" Eso es basura. Ahora no solo tiene un Objeto de Dios que maneja todas estas cosas, sino que está escribiendo su código en XML (o similar) donde no obtiene análisis estático, no obtiene errores de tiempo de compilación y no lo hace ' Realmente obtengo pruebas unitarias efectivas.

Me temo que es un ripost muy teórico. En la práctica, a la mayoría de los desarrolladores e integradores de sistemas les gusta el hecho de que puede configurar el registro a través de un archivo de configuración. Y les GUSTA el hecho de que no se espera que prueben unitariamente el registro de un módulo.

Claro, si rellena las configuraciones de registro, entonces puede tener problemas, pero se manifestarán ya sea como la aplicación falla durante el inicio o demasiado o muy poco registro. 1) Estos problemas se solucionan fácilmente solucionando el error en el archivo de configuración. 2) La alternativa es un ciclo completo de compilación / análisis / prueba / implementación cada vez que realice un cambio en los niveles de registro. Eso no es aceptable.

Stephen C
fuente
3

y No !

Sí: creo que es razonable que diferentes subsistemas (o capas semánticas o bibliotecas u otras nociones de agrupación modular) acepten (el mismo o) registrador potencialmente diferente durante su inicialización en lugar de que todos los subsistemas confíen en el mismo singleton compartido común .

Sin embargo,

No: al mismo tiempo no es razonable parametrizar el registro en cada pequeño objeto (por constructor o método de instancia). Para evitar la hinchazón innecesaria e inútil, las entidades más pequeñas deben usar el registrador singleton de su contexto de cierre.


Esta es una razón entre muchas otras para pensar en la modularidad en los niveles: los métodos se agrupan en clases, mientras que las clases se agrupan en subsistemas y / o capas semánticas. Estos paquetes más grandes son valiosas herramientas de abstracción; debemos dar diferentes consideraciones dentro de los límites modulares que al cruzarlos.

Erik Eidt
fuente
3

Primero, comienza con una memoria caché de singleton fuerte, lo siguiente que ve son los singletons fuertes para la capa de base de datos que introduce estado global, API no descriptivas de classes y código no comprobable.

Si decide no tener un singleton para una base de datos, probablemente no sea una buena idea tener un singleton para una memoria caché, después de todo, representan un concepto muy similar, el almacenamiento de datos, que solo usa mecanismos diferentes.

El uso de un singleton en una clase convierte una clase que tiene una cantidad específica de dependencias a una clase que tiene, teóricamente , un número infinito de ellas, porque nunca se sabe qué está realmente oculto detrás del método estático.

En la última década que pasé programando, solo hubo un caso en el que presencié un esfuerzo para cambiar la lógica de registro (que luego se escribió como singleton). Entonces, aunque me encantan las inyecciones de dependencia, el registro no es realmente una gran preocupación. Caché, por otro lado, definitivamente siempre lo haría como una dependencia.

Andy
fuente
Sí, el registro rara vez cambia y generalmente no tiene que ser un módulo intercambiable. Sin embargo, una vez intenté probar una unidad auxiliar de registro, que tenía una dependencia estática del sistema de registro. Decidí que el mecanismo más fácil para hacerlo comprobable sería ejecutar la clase bajo prueba en un proceso separado, configurar su registrador para escribir en STDOUT y analizar esa salida en mi caso de prueba ಠ_ಠ He tenido una experiencia similar con los relojes donde ' obviamente, nunca quiero nada más que el tiempo real, ¿verdad? Excepto cuando se prueban casos de borde de zona horaria / horario de verano, por supuesto ...
amon
@amon: Los relojes son como iniciar sesión que ya existe otro mecanismo que sirve para el mismo propósito que DI, a saber, Joda-Time y sus numerosos puertos. (No es que haya mal nada con el uso de DI lugar;. Pero es más fácil de usar Joda-Time directamente en vez de tratar de escribir un adaptador inyectable costumbre, y nunca he visto a nadie pesar hacerlo)
ruakh
@amon "He tenido una experiencia similar con los relojes en los que obviamente nunca querría nada más que el tiempo real, ¿verdad? Excepto cuando prueba casos de zona horaria / borde de horario de verano, por supuesto ..." - o cuando se da cuenta de que un error ha dañó su base de datos y la única esperanza de recuperarla es analizar su registro de eventos y reproducirlo a partir de la última copia de seguridad ... pero de repente necesita que todo su código funcione en función de la marca de tiempo de la entrada de registro actual en lugar de la actual hora.
Jules
3

Sí y no, pero principalmente no

Supongo que la mayor parte de la conversación se basa en una instancia estática vs inyectada. ¿Nadie propone que el registro rompa el SRP, supongo? Estamos hablando principalmente del "principio de inversión de dependencia". Tbh, estoy de acuerdo con la respuesta de Telastyn.

¿Cuándo está bien usar estadísticas? Porque claramente a veces está bien. El punto de respuestas afirmativas de los beneficios de la abstracción y las respuestas de "no" indican que son algo por lo que usted paga. Una de las razones por las que su trabajo es difícil es que no hay una respuesta que pueda escribir y aplicar a todas las situaciones.

Tomar: Convert.ToInt32("1")

Prefiero esto a:

private readonly IConverter _converter;

public MyClass(IConverter converter)
{
   Guard.NotNull(converter)
   _converter = conveter
}

.... 
var foo = _converter.ToInt32("1");

¿Por qué? Estoy aceptando que necesitaré refactorizar el código si necesito la flexibilidad para cambiar el código de conversión. Estoy aceptando que no podré burlarme de esto. Creo que la sencillez y la brevedad valen este oficio.

Mirando el otro extremo del espectro, si IConverterfuera un SqlConnection, me horrorizaría mucho ver eso como una llamada estática. Las razones por las cuales son muy obvias. Señalaría que a SQLConnectionpuede ser bastante "transversal" en una aplicación, por lo que no habría usado esas palabras exactas.

¿El registro se parece más a un SQLConnectiono Convert.ToInt32? Yo diría más como 'SQLConnection`.

Deberías burlarte de Logging . Habla con el mundo exterior. Cuando escribo un método usando Convert.ToIn32, lo estoy usando como una herramienta para calcular algún otro resultado de la clase que pueda probarse por separado. No necesito verificar si Convertse llamó correctamente al verificar que "1" + "2" == "3". El registro es diferente, es una salida totalmente independiente de la clase. Supongo que es un resultado que tiene valor para usted, el equipo de soporte y el negocio. Su clase no funciona si el registro no es correcto, por lo que las pruebas unitarias no deberían pasar. Deberías probar lo que registra tu clase. Creo que este es el argumento asesino, realmente podría parar aquí.

También creo que es algo que es muy probable que cambie. Un buen registro no solo imprime cadenas, es una vista de lo que está haciendo su aplicación (soy un gran admirador del registro basado en eventos). He visto que el registro básico se convierte en IU de informes bastante elaboradas. Obviamente, es mucho más fácil ir en esta dirección si su registro se parece _logger.Log(new ApplicationStartingEvent())y no se parece Logger.Log("Application has started"). Uno podría argumentar que esto está creando un inventario para un futuro que tal vez nunca suceda, esta es una decisión y creo que vale la pena.

De hecho, en un proyecto personal mío, creé una interfaz de usuario sin inicio de sesión utilizando únicamente el _loggerpara averiguar qué estaba haciendo la aplicación. Esto significaba que no tenía que escribir código para averiguar qué estaba haciendo la aplicación, y terminé con un registro sólido. Siento que si mi actitud hacia el registro fuera simple e inmutable, esa idea no se me habría ocurrido.

Así que estaría de acuerdo con Telastyn para el caso de la tala.

Nathan Cooper
fuente
Así es como me conecto incidentalmente . Me gustaría vincular a un artículo o algo, pero no puedo encontrar uno. Si se encuentra en el mundo .NET y busca el registro de eventos, lo encontrará Semantic Logging Application Block. No lo use, como la mayoría del código creado por el equipo de "patrones y práctica" de MS, irónicamente, es un antipatrón.
Nathan Cooper
3

Primeras preocupaciones transversales no son bloques de construcción importantes y no deben tratarse como dependencias en un sistema. Un sistema debería funcionar si, por ejemplo, Logger no está inicializado o la memoria caché no funciona. ¿Cómo hará que el sistema sea menos acoplado y cohesivo? Ahí es donde SOLID aparece en el diseño del sistema OO.

Mantener el objeto como singleton no tiene nada que ver con SOLID. Ese es el ciclo de vida de su objeto cuánto tiempo desea que el objeto viva en la memoria.

Una clase que necesita una dependencia para inicializarse no debería saber si la instancia de clase proporcionada es singleton o transitoria. Pero tldr; si está escribiendo Logger.Instance.Log () en cada método o clase, entonces es un código problemático (olor a código / acoplamiento duro) realmente desordenado. Este es el momento en que las personas comienzan a abusar de SOLID. Y otros desarrolladores como OP comienzan a hacer preguntas genuinas como esta.

vendettamit
fuente
2

He resuelto este problema usando una combinación de herencia y rasgos (también llamados mixins en algunos idiomas). Los rasgos son muy útiles para resolver este tipo de preocupación transversal. Sin embargo, generalmente es una característica del lenguaje, así que creo que la respuesta real es que depende de las características del lenguaje.

RibaldEddie
fuente
Interesante. Nunca he hecho un trabajo significativo utilizando un lenguaje que admita rasgos, pero he considerado usar dichos lenguajes para algunos proyectos futuros, por lo que sería útil para mí si puede ampliar esto y mostrar cómo los rasgos resuelven el problema.
Jules