Cuándo usar DI y cuándo crearse en Java

8

Tengo una buena cantidad de OOP con varios idiomas, pero soy bastante nuevo en Java.

Estoy leyendo una gran cantidad de tutoriales donde se crean grandes cantidades de objetos dentro del código de una clase, y estoy tratando de ejecutarlos, pero construyo versiones de las clases en los tutoriales que hacen la Inyección de dependencia en lugar de instanciar todos los clases ellos mismos.

Pero Java no es como otros lenguajes que he usado en que casi todo es un objeto. Si tuviera que inyectar literalmente todo, entonces el resultado sería muy complicado y difícil de seguir.

Obviamente no inyectarías objetos String, y supongo que hay otros objetos que no inyectarías, pero no estoy seguro de dónde debe ir la línea. ¿En qué momento DI deja de ser lo correcto y cuándo comienza a ser una carga? ¿Cómo se decide pragmáticamente qué inyectar y qué crear una instancia?

Para su información, los tutoriales que estoy haciendo son http://edn.embarcadero.com/article/31995 y http://docs.oracle.com/javase/tutorial/networking/sockets/clientServer.html para crear un cliente simple y servidor. Sin embargo, no los estoy copiando línea por línea, estoy tratando de crear clases equivalentes que sigan las mejores prácticas

GordonM
fuente
1
Esta pregunta no es Java, pero habla de formas de 'inyectar todo' de formas menos desordenadas: programmers.stackexchange.com/questions/190120/…
JeffO

Respuestas:

4

En .Net (que es similar a este respecto que todo es un objeto), utilizo DI principalmente con mis objetos de dominio (entidades, repositorios y servicios) y objetos de aplicación (controladores, modelos de vista), por lo que los repos / servicios se inyectan en el controlador / ver modelos y similares. Esto significa que mi lógica empresarial y de aplicación es totalmente comprobable, que es la razón principal por la que uso DI.

Básicamente, los objetos en los que se inyecta e inyecta material son en su mayoría objetos específicos de la solución que está creando. Si algún objeto es "nativo" del lenguaje o marco (como .Net), probablemente no sea necesario inyectarlo. Muchos objetos necesitan una cadena para representar datos, y una cadena es básicamente una "característica de lenguaje" que no es específica de su dominio. Lo mismo se aplica a los enumerables, las listas, las matrices y muchas otras cosas que no necesitan cambiar cuando desea ejecutar su clase de forma aislada del resto del sistema.

Lo más importante a tener en cuenta es el acoplamiento entre sus clases de dominio / aplicación. Si un objeto no introduce un acoplamiento apretado, generalmente es seguro crearlo. Una de las formas de verificar el acoplamiento es intentar ejecutar los métodos en una clase de forma aislada (que es básicamente una prueba unitaria). Lo que debe ser DI-ed son todas las dependencias de las que necesita burlarse, para asegurarse de que la clase que está probando realmente se ejecute aisladamente del sistema.

Ivan Pintar
fuente
Otra forma de verlo es ¿qué es lo que tu clase o tu método están tratando de hacer? El ejemplo clásico es que desea guardar un objeto. La lógica en esa clase / método debe ser sobre guardar el objeto, no apagarse y hacer el trabajo de la placa de calderas para crear conexiones, etc., ese tipo de servicio / recurso podría / debería ser DI.
Martijn Verburg
4

En primer lugar, está bien usar la nueva palabra clave, ¡de verdad! La DI es una técnica fantástica, pero existe la tentación de usarla en exceso. Los momentos en que es posible que desee utilizar DI son:

  • Dobles de prueba: a menudo, con las pruebas unitarias, desea simular / probar el doble de un recurso de terceros ya que no desea activar ese recurso de terceros (por ejemplo, una base de datos Oracle completa). Por ejemplo, en lugar de crear una nueva conexión de base de datos a la gran base de datos Oracle, es posible que desee reemplazar esa conexión con una a un HSQL (en la base de datos de memoria).

  • Cuando desea controlar el alcance (~ vida útil) de ese objeto y el ciclo de vida natural de los objetos Java no funciona para usted. Muchos frameworks DI basados ​​en Java le permiten hacer esto.

  • Cuando quieres solteros.

  • Cada vez que crea que necesita una Fábrica, un Localizador de servicios o similar. Esto incluye si necesita diferentes fábricas (raro, pero sucede).

En otras ocasiones, simplemente cree un nuevo objeto, de hecho, si tiene dudas, cree un nuevo objeto; siempre puede DI cuando lo necesite más tarde :-).

Martijn Verburg
fuente
Tu primer punto PODRÍA significar absolutamente cualquier cosa, así que no estoy seguro de cómo has respondido la pregunta. Y he visto muchos lugares donde usar DI para inyectar una Fábrica, en lugar de tratar de construir una Fábrica a través de DI, es la mejor solución, por lo que también tengo dudas sobre el último punto.
pdr
Editado para aclarar un poco los puntos
Martijn Verburg
Nunca escuché la frase Test Doubles, pensé que querías decir doc.oracle.com/javase/6/docs/api/java/lang/Double.html .
NimChimpsky
1
@NimChimpsky un doble de prueba es un término genérico para cualquier simulacro, trozo u otro sustituto. xunitpatterns.com/Test%20Double.html
0

De Wikipedia :

El propósito principal del patrón de inyección de dependencia es permitir la selección entre múltiples implementaciones de una interfaz de dependencia dada en tiempo de ejecución o mediante archivos de configuración, en lugar de en tiempo de compilación. El patrón es particularmente útil para proporcionar implementaciones de prueba de código auxiliar de componentes complejos durante las pruebas, pero a menudo se usa para localizar componentes de complementos o para ubicar e inicializar

No debería necesitar inyectar cadenas, simplemente puede pasarlas como parámetros al método relevante. No debería necesitar inyectar ningún dato, aparte de config.

Las buenas pruebas de unidad lo ayudan a localizar errores y, por lo tanto, debe probar su código independientemente de otras unidades de código. DI puede ayudarlo a lograr esto.

Dave Hillier
fuente
0

En general, encuentro que una clase solo debería ser newde otra clase.

Esto se debe a que una vez que va por el camino de la inyección de dependencias, cada objeto que usa newtiene que inyectar todas las dependencias de los objetos que crea, lo que significa que el objeto padre también debe conocer todas esas dependencias. Si Atiene una dependencia By Ccrea Ainstancias, newentonces Cautomáticamente necesita tener una dependencia B, incluso si Cnunca se usa Bdirectamente. Primero, eso viola la separación de preocupaciones, a menos que Cel único trabajo sea crear As. De lo contrario, crea un problema si alguna vez desea agregar una dependencia Aporque todas las cosas que la crean ahora necesitan agregar esa misma dependencia. Realmente no deberían preocuparse por eso. Es mejor tenerCdeclarar una dependencia AFactoryy AFactorytener una dependencia en B. Luego puede agregar dependencias a Atodo lo que desee, y solo AFactorytiene que cambiar.

Scott Whitlock
fuente
-1

"¿En qué momento DI deja de ser lo correcto y cuándo comienza a ser una carga? ..."

En primer lugar, me gusta la DI y la uso mucho, así que no me malinterpreten

DI es excelente para el lado del servidor, pero los juegos cambian todos juntos cuando se trata de programación Gui. Por ejemplo, incorporé Guice a una aplicación GUI existente desarrollada en Swing

  • Necesita refactorizar mucho para deshacerse de cosas como fábricas estáticas, dependencias anidadas, etc.
  • Debe ser consciente de cómo el contenedor DI crea / maneja objetos, por ejemplo, si crea objetos ansiosamente al inicio, entonces puede tener problemas de rendimiento (puede ser cualquier aplicación, pero particularmente en aplicaciones GUI 15-40 segundos de retraso importa mucho). Puede modificar la configuración siempre que el contenedor DI lo permita.
  • Para incorporar DI en un proyecto existente, necesita revolución que evolución

¿Cómo manejar las dependencias anidadas?

supongamos que necesita acceder a algunas dependencias en clases profundamente anidadas, puede usar DI de una de las siguientes maneras:

  • ¿Modificar el constructor para tener la inyección del constructor (así que sabemos que terminamos con el constructor tomando muchos parámetros ...)?
  • Use la inyección de campo (que no es una buena solución porque estamos exponiendo nuestros campos)
  • Use la inyección de setter (sepa que necesitamos crear setters innecesarios en nuestro código)

¿Qué tal si desea crear un objeto usando new () (porque el objeto necesita ser creado en tiempo de ejecución dependiendo de algunos criterios) y desea registrarlo en el contenedor (Guice no maneja objetos creados con new (), que es bueno ) Por lo tanto, ahora debe volver a centrarse en el contenedor en lugar de desarrollar nuevas funciones en su aplicación.

Palabras finales en algunos casos usando DesignPatterns + pensamiento creativo y buen diseño de arquitectura, puede evitar situaciones donde la ganancia es pequeña y las molestias son altas como resultado de la introducción de DI

sol4me
fuente