Recientemente trabajé en un proyecto de Python donde realizamos una gran inyección de dependencias (porque debemos hacerlo para que la aplicación sea comprobable), pero no utilizamos ningún marco. A veces era un poco tedioso conectar todas las dependencias manualmente, pero en general funcionó muy bien.
Cuando un objeto tenía que ser creado en múltiples lugares, simplemente teníamos una función (a veces un método de clase de ese objeto) para crear una instancia de producción. Esta función se llamaba cuando necesitábamos ese objeto.
En las pruebas, hicimos lo mismo: si varias veces necesitábamos crear una 'versión de prueba' de un objeto (es decir, una instancia real respaldada por objetos simulados), teníamos una función para crear esta 'versión de prueba' de una clase, para ser utilizado en pruebas.
Como dije, generalmente esto funcionó bien.
Ahora estoy ingresando a un nuevo proyecto Java, y estamos decidiendo si usar un marco DI o hacer DI manualmente (como en el proyecto anterior).
Por favor explique cómo será diferente trabajar con un marco DI y de qué manera es mejor o peor que la inyección manual de dependencia 'vainilla'.
Editar: También me gustaría agregar a la pregunta: ¿ha sido testigo de algún proyecto de 'nivel profesional' que realiza DI manualmente, sin un marco?
fuente
Respuestas:
La clave de los contenedores DI es la abstracción . Los contenedores DI resumen esta preocupación de diseño por usted. Como puede suponer, tiene un impacto notable en el código, a menudo traducido en un menor número de constructores, instaladores, fábricas y constructores. En consecuencia, hacen que su código esté más débilmente acoplado (debido a la IoC subyacente ), más limpio y simple. Aunque no sin un costo.
Antecedentes
Hace años, tal abstracción requería que escribiéramos varios archivos de configuración ( programación declarativa ). Fue fantástico ver que el número de LOC disminuía sustancialmente, pero aunque los LOCS disminuyeron, el número de archivos de configuración aumentó ligeramente. Para proyectos medianos o pequeños no fue un problema en absoluto, pero para proyectos más grandes, tener el "ensamblaje del código" disperso al 50% entre el código y los descriptores XML se convirtió en una molestia en ... Sin embargo, el problema principal fue paradigma en sí mismo. Era bastante inflexible porque los descriptores dejaban poco espacio para las personalizaciones.
El paradigma cambió progresivamente hacia la convención sobre la configuración , que intercambia archivos de configuración por anotaciones o atributos. Mantiene nuestro código más limpio y simple a la vez que nos proporciona la flexibilidad que XML no podría.
Probablemente, esta es la diferencia más significativa con respecto a cómo trabajaba hasta ahora. Menos código y más magia.
A la baja...
La convención sobre la configuración hace que la abstracción sea tan pesada que apenas se sabe cómo funciona. A veces parece mágico y aquí viene un inconveniente más. Una vez que funciona, a muchos desarrolladores no les importa cómo funciona.
Esta es una desventaja para aquellos que aprendieron DI con contenedores DI. No entienden completamente la relevancia de los patrones de diseño, las buenas prácticas y los principios abstraídos por el contenedor. Le pregunté a los desarrolladores si estaban familiarizados con DI y sus ventajas y respondieron: Sí, he usado Spring IoC . (¿¡¿Que demonios significa?!?)
Uno puede estar de acuerdo o no con cada uno de estos "inconvenientes". En mi humilde opinión, es imprescindible saber qué está pasando en su proyecto. De lo contrario, no tenemos una comprensión completa de ello. También es saber para qué sirve la DI y tener una idea de cómo implementarla es una ventaja a tener en cuenta.
En el lado positivo...
Una palabra de productividad . Los marcos nos permiten centrarnos en lo que realmente importa. El negocio de la aplicación.
A pesar de las deficiencias comentadas, para muchos de nosotros (qué trabajo está condicionado por plazos y costos), estas herramientas son un recurso invaluable. En mi opinión, este debería ser nuestro argumento principal a favor de implementar un contenedor DI. Productividad .
Pruebas
Ya sea que implementemos DI o contenedores puros, las pruebas no serán un problema. Todo lo contrario. Los mismos contenedores a menudo nos proporcionan los simulacros para las pruebas unitarias fuera de la caja. A lo largo de los años he usado mis pruebas como termómetros. Me dicen si he dependido mucho de las instalaciones de contenedores. Digo esto porque estos contenedores pueden inicializar atributos privados sin establecedores o constructores. ¡Pueden inyectar componentes casi en cualquier lugar!
Suena tentador ¿verdad? ¡Ten cuidado! ¡No caigas en la trampa!
Sugerencias
Si decide implementar contenedores, le sugiero que siga las buenas prácticas. Siga implementando constructores, setters e interfaces. Aplicar el marco / contenedor para usarlos . Facilitará las migraciones posibles a otro marco o eliminará el actual. Puede reducir significativamente la dependencia del contenedor.
Con respecto a estas prácticas:
Cuándo usar contenedores DI
Mi respuesta estará sesgada por mi propia experiencia, que está principalmente a favor de los contenedores DI (como comenté, estoy muy centrado en la productividad, por lo que nunca he tenido la necesidad de una DI pura. Todo lo contrario).
Creo que encontrará interesante el artículo de Mark Seeman sobre este tema, que también responde a la pregunta sobre cómo implementar la DI pura .
Finalmente, si estuviéramos hablando de Java, consideraría primero echar un vistazo al JSR330 - Inyección de dependencia .
Resumiendo
Beneficios :
Inconvenientes :
Diferencias con DI pura :
fuente
En mi experiencia, un marco de inyección de dependencia conduce a una serie de problemas. Algunos de los problemas dependerán del marco y las herramientas. Puede haber prácticas que mitiguen los problemas. Pero estos son los problemas que he encontrado.
Objetos no globales
En algunos casos, desea inyectar un objeto global: un objeto que tendrá solo una instancia en su aplicación en ejecución. Esta podría ser una
MarkdownToHtmlRendererer
, unaDatabaseConnectionPool
, la dirección de su servidor api, etc. Para estos casos, los marcos de inyección de dependencias funcionan de manera muy simple, simplemente agrega la dependencia necesaria a su constructor y lo tiene.¿Pero qué pasa con los objetos que no son globales? ¿Qué sucede si necesita al usuario para la solicitud actual? ¿Qué sucede si necesita la transacción de base de datos actualmente activa? ¿Qué sucede si necesita el elemento HTML correspondiente al div que contiene un formulario en el que hay un cuadro de texto que acaba de disparar un evento?
La solución que he visto empleada por los marcos DI son los inyectores jerárquicos. Pero esto hace que el modelo de inyección sea más complicado. Se hace más difícil determinar qué se inyectará desde qué capa de la jerarquía. Se hace difícil saber si un objeto dado existirá por aplicación, por usuario, por solicitud, etc. Ya no puede simplemente agregar sus dependencias y hacer que funcione.
Bibliotecas
A menudo querrás tener una biblioteca de código reutilizable. Esto también incluirá las instrucciones sobre cómo construir objetos a partir de ese código. Si usa un marco de inyección de dependencia, esto generalmente incluirá reglas vinculantes. Si desea utilizar la biblioteca, agregue sus reglas vinculantes a las suyas.
Sin embargo, pueden surgir varios problemas. Puede terminar con la misma clase vinculada a diferentes implementaciones. La biblioteca puede esperar que existan ciertos enlaces. La biblioteca puede anular algunos enlaces predeterminados haciendo que su código haga cosas extrañas. Su código puede anular algunos enlaces predeterminados haciendo que el código de la biblioteca haga cosas extrañas.
Complejidad oculta
Cuando escribe fábricas manualmente, tiene una idea de cómo encajan las dependencias de la aplicación. Cuando usa un marco de inyección de dependencia, no ve esto. En cambio, los objetos tienden a crecer más y más dependencias hasta que tarde o temprano todo depende de casi todo.
Soporte IDE
Su IDE probablemente no comprenderá el marco de inyección de dependencias. Con las fábricas manuales, puede encontrar todas las personas que llaman de un constructor para descubrir todos los lugares donde el objeto podría construirse. Pero no encontrará las llamadas generadas por el marco DI.
Conclusión
¿Qué obtenemos de un marco de inyección de dependencia? Podemos evitar algunas repeticiones simples necesarias para crear instancias de objetos. Estoy a favor de eliminar repeticiones simples. Sin embargo, mantener una placa repetitiva simple es fácil. Si vamos a reemplazar esa placa repetitiva, debemos insistir en reemplazarla con algo más fácil. Debido a los diversos problemas que he discutido anteriormente, no creo que los marcos (al menos tal como están) se ajusten a la ley.
fuente
¿Se requiere Java + dependency injection = framework?
No.
¿Qué me compra un marco DI?
Construcción de objetos que ocurre en un lenguaje que no es Java.
Si los métodos de fábrica son lo suficientemente buenos para sus necesidades, puede hacer DI en java completamente sin marco en pojo java 100% auténtico. Si sus necesidades van más allá, tiene otras opciones.
Los patrones de diseño de Gang of Four logran muchas cosas grandiosas, pero siempre han tenido debilidades cuando se trata de los patrones de creación. Java en sí tiene algunas debilidades aquí. Los marcos DI realmente ayudan con esta debilidad creacional más de lo que lo hacen con las inyecciones reales. Ya sabes cómo hacerlo sin ellos. Cuánto bien le harán dependerá de cuánta ayuda necesite con la construcción de objetos.
Hay muchos patrones de creación que surgieron después de la Banda de los Cuatro. El constructor Josh Bloch es un truco maravilloso en torno a la falta de parámetros con nombre de Java. Más allá de eso están los creadores de iDSL que ofrecen mucha flexibilidad. Pero cada uno de estos representa un trabajo significativo y repetitivo.
Los marcos pueden salvarte de algo de este tedio. No están exentos de inconvenientes. Si no tiene cuidado, puede depender del marco. Intente cambiar marcos después de usar uno y compruébelo usted mismo. Incluso si de alguna manera mantiene el xml o las anotaciones genéricas, puede terminar admitiendo lo que son esencialmente dos lenguajes que debe mencionar uno al lado del otro cuando anuncia trabajos de programación.
Antes de enamorarse demasiado del poder de los marcos de DI, tómese un tiempo e investigue otros marcos que le brinden capacidades para las que de otro modo podría pensar que necesita un marco de DI. Muchos se han ramificado más allá de la simple DI . Considere: Lombok , AspectJ y slf4J . Cada uno se superpone con algunos marcos DI pero cada uno funciona bien por sí solo.
Si la prueba es realmente la fuerza impulsora detrás de esto, investigue: jUnit (cualquier xUnit realmente) y Mockito (cualquier simulación realmente) antes de comenzar a pensar que un marco DI debería tomar el control de su vida.
Puedes hacer lo que quieras, pero para un trabajo serio prefiero una caja de herramientas bien poblada a una navaja suiza. Desearía que fuera más fácil dibujar estas líneas, pero algunos han trabajado duro para desenfocarlas. No tiene nada de malo, pero puede hacer que estas elecciones sean un desafío.
fuente
Crear clases y asignarles dependencias es parte del proceso de modelado, las partes divertidas. Es la razón por la cual la mayoría de los programadores disfrutan de la programación.
Decidir qué implementación se usará y cómo se construirán las clases es algo que tendrá que implementarse de alguna manera y es parte de la configuración de la aplicación.
Escribir fábricas manualmente es un trabajo tedioso. Los marcos DI lo hacen más fácil al resolver automáticamente las dependencias que pueden resolverse y requieren configuración para aquellas que no.
El uso de IDEs modernos le permite navegar a través de métodos y sus usos. Tener fábricas escritas manualmente es bastante bueno, porque simplemente puede resaltar el constructor de una clase, mostrar sus usos y le mostrará todos los lugares donde y cómo se está construyendo la clase específica.
Pero incluso con el marco DI, puede tener un lugar central, como la configuración de construcción de gráfico de objetos (OGCC de ahora en adelante), y si una clase depende de dependencias ambiguas, simplemente puede mirar en OGCC y ver cómo se está construyendo una clase específica justo ahí.
Podrías objetar, que personalmente crees que poder ver los usos de un constructor específico es una gran ventaja. Diría que lo que te resulta mejor no solo es subjetivo, sino que proviene principalmente de la experiencia personal.
Si siempre hubiera trabajado en proyectos que se basaran en marcos DI, solo lo sabría y cuando quisiera ver la configuración de una clase, siempre miraría en OGCC y ni siquiera trataría de ver cómo se usan los constructores , porque sabría que todas las dependencias están conectadas automáticamente de todos modos.
Naturalmente, los marcos DI no se pueden usar para todo y de vez en cuando tendrá que escribir uno o dos métodos de fábrica. Un caso muy común es construir una dependencia durante la iteración sobre una colección, donde cada iteración proporciona un indicador diferente que debería producir una implementación diferente de una interfaz común.
Al final, lo más probable es que todo se reduzca a cuáles son sus preferencias y a lo que está dispuesto a sacrificar (ya sea tiempo escribiendo fábricas o transparencia).
Experiencia personal (advertencia: subjetiva)
Hablando como desarrollador de PHP, aunque me gusta la idea detrás de los marcos DI, solo los he usado una vez. Fue entonces cuando se suponía que un equipo con el que estaba trabajando desplegaría una aplicación muy rápidamente y no podríamos perder el tiempo escribiendo fábricas. Así que simplemente recogimos un marco DI, lo configuramos y funcionó, de alguna manera .
Para la mayoría de los proyectos, me gusta que las fábricas se escriban manualmente porque no me gusta la magia negra que ocurre dentro de los marcos DI. Yo fui el responsable de modelar una clase y sus dependencias. Yo decidí por qué el diseño se ve como se ve. Así que seré yo quien construya correctamente la clase (incluso si la clase toma dependencias difíciles, como clases concretas en lugar de interfaces).
fuente
Personalmente no uso contenedores DI y no me gustan. Tengo un par de razones más para eso.
Por lo general, es una dependencia estática. Por lo tanto, es solo un localizador de servicios con todos sus inconvenientes. Luego, debido a la naturaleza de las dependencias estáticas, se ocultan. No necesita tratar con ellos, de alguna manera ya están allí, y es peligroso, porque deja de controlarlos.
Se rompe los principios de programación orientada a objetos , como la cohesión y David West Lego-ladrillo metáfora 's.
Así que construir el objeto completo lo más cerca posible del punto de entrada del programa es el camino a seguir.
fuente
Como ya habrás notado: dependiendo del lenguaje que uses, hay diferentes puntos de vista, diferentes enfoques sobre cómo lidiar con problemas similares.
Con respecto a la inyección de dependencia, todo se reduce a esto: la inyección de dependencia es, como dice el término, nada más que poner las dependencias que un objeto necesita en ese objeto , lo que contrasta de otra manera: dejar que el objeto mismo instale instancias de objetos de los que depende. Desacopla la creación de objetos y el consumo de objetos para ganar más flexibilidad.
Si su diseño está bien diseñado, generalmente tiene pocas clases con muchas dependencias, por lo tanto, el cableado de las dependencias, ya sea a mano o mediante un marco, debería ser relativamente fácil y la cantidad de repeticiones para escribir las pruebas debería ser fácil de manejar.
Dicho esto, no hay necesidad de un marco DI.
Pero, ¿por qué quieres usar un marco de todos modos?
I) Evite el código repetitivo
Si su proyecto obtiene un tamaño, donde siente el dolor de escribir fábricas (e instancias) una y otra vez, debe usar un marco DI
II) Enfoque declarativo de la programación
Cuando Java una vez estaba programando con XML , ahora es: rociando polvo de feria con anotaciones y sucede la magia .
Pero en serio: veo una clara ventaja de esto. Comunicas claramente la intención al decir lo que se necesita en lugar de dónde encontrarlo y cómo construirlo.
tl; dr
Los marcos DI no hacen que su base de código sea mágicamente mejor, pero ayuda a que el código de buena apariencia se vea realmente nítido . Úselo cuando sienta que lo necesita. En un momento determinado de su proyecto, le ahorrará tiempo y evitará las náuseas.
fuente
Sí. Probablemente deberías leer el libro de Mark Seemann titulado "Inyección de dependencia". Él describe algunas buenas prácticas. Con base en algo de lo que ha dicho, podría beneficiarse. Debe intentar tener una raíz de composición o una raíz de composición por unidad de trabajo (por ejemplo, solicitud web). Me gusta su forma de pensar que cualquier objeto que cree se considera una dependencia (como cualquier parámetro de un método / constructor), por lo que realmente debe tener cuidado con dónde y cómo crear objetos. Un marco lo ayudará a mantener claros estos conceptos. Si lees el libro de Mark, encontrarás más que solo la respuesta a tu pregunta.
fuente
Sí, cuando trabaje en Java, recomendaría un marco DI. Hay algunas razones para esto:
Java tiene varios paquetes DI extremadamente buenos que ofrecen inyección de dependencia automatizada y, por lo general, vienen con capacidades adicionales que son más difíciles de escribir manualmente que la DI simple (por ejemplo, dependencias con ámbito que permiten la conexión automática entre ámbitos mediante la generación de código para buscar en qué ámbito debería estar) use d para encontrar la versión correcta de la dependencia para ese ámbito, por lo que, por ejemplo, los objetos con ámbito de aplicación pueden referirse a los con ámbito de solicitud).
Las anotaciones de Java son extremadamente útiles y proporcionan una excelente manera de configurar dependencias
la construcción de objetos java es demasiado detallada en comparación con lo que estás acostumbrado en python; El uso de un marco aquí ahorra más trabajo del que haría en un lenguaje dinámico.
Los frameworks java DI pueden hacer cosas útiles como generar automáticamente proxies y decoradores, que son más trabajo en Java que un lenguaje dinámico.
fuente
Si su proyecto es lo suficientemente pequeño y no tiene mucha experiencia con el DI-Framework, recomendaría no usar el framework y hacerlo manualmente.
Motivo: una vez trabajé en un proyecto que utilizaba dos bibliotecas que tenían dependencias directas a diferentes marcos de trabajo. ambos tenían un código específico de framework como di-give-me-an-instance-of-XYZ. Hasta donde sé, solo puede tener una implementación activa de marco de trabajo a la vez.
Con un código como este puede hacer más daño que beneficio.
Si tiene más experiencia, es probable que abstraiga el marco de trabajo mediante una interfaz (fábrica o localizador de servicios) para que este problema ya no exista y el marco de trabajo beneficiará más ese daño.
fuente