Sé que este es un debate candente y las opiniones tienden a cambiar con el tiempo en cuanto a la mejor práctica de enfoque.
Solía usar exclusivamente la inyección de campo para mis clases, hasta que empecé a leer sobre diferentes blogs (ejs: petrikainulainen y schauderhaft y Fowler ) sobre los beneficios de la inyección de constructor. Desde entonces he cambiado mis metodologías para usar la inyección de constructor para las dependencias requeridas y la inyección de setter para dependencias opcionales.
Sin embargo, recientemente me he metido en un debate con el autor de JMockit, un marco burlón, en el que ve la inyección del constructor y del instalador como una mala práctica e indica que la comunidad JEE está de acuerdo con él.
En el mundo de hoy, ¿hay una forma preferida de hacer la inyección? ¿Se prefiere la inyección de campo?
Después de cambiar a inyección de constructor desde la inyección de campo en los últimos años, me resulta mucho más fácil de usar, pero me pregunto si debería volver a examinar mi punto de vista. El autor de JMockit (Rogério Liesenfeld) , está claramente versado en DI, por lo que me siento obligado a revisar mi enfoque dado que se siente muy en contra de la inyección del constructor / instalador.
fuente
Respuestas:
La inyección de campo es un poco demasiado "acción espeluznante a distancia" para mi gusto.
Considere el ejemplo que proporcionó en su publicación de Grupos de Google:
Entonces, básicamente, lo que está diciendo es que "tengo esta clase con estado privado, a la que he adjuntado
@injectable
anotaciones, lo que significa que el estado puede ser poblado automáticamente por algún agente externo, a pesar de que mi estado ha sido declarado privado". "Entiendo las motivaciones para esto. Es un intento de evitar gran parte de la ceremonia que es inherente a organizar una clase correctamente. Básicamente, lo que uno dice es que "estoy cansado de escribir toda esta repetitiva, así que voy a anotar todo mi estado y dejar que el contenedor DI se encargue de prepararlo para mí".
Es un punto de vista perfectamente válido. Pero también es una solución alternativa para las características del lenguaje que posiblemente no deberían evitarse. Además, ¿por qué parar allí? Tradicionalmente, DI confía en que cada clase tenga una interfaz complementaria. ¿Por qué no eliminar todas esas interfaces con anotaciones también?
Considere la alternativa (esto será C #, porque lo conozco mejor, pero probablemente haya un equivalente exacto en Java):
Ya sé algunas cosas sobre esta clase. Es una clase inmutable; solo se puede establecer en el constructor (referencias, en este caso particular). Y debido a que todo se deriva de una interfaz, puedo intercambiar implementaciones en el constructor, que es donde entran tus simulacros.
Ahora todo lo que mi contenedor DI tiene que hacer es reflexionar sobre el constructor para determinar qué objetos necesita para renovarse. Pero esa reflexión se está haciendo en un miembro público, de una manera de primera clase; es decir, los metadatos ya forman parte de la clase, habiéndose declarado en el constructor, un método cuyo propósito expreso es proporcionar a la clase las dependencias que necesita.
De acuerdo, esto es un montón de repeticiones, pero así es como se diseñó el lenguaje. Las anotaciones parecen un truco sucio para algo que debería haberse incorporado al lenguaje mismo.
fuente
VeracodeService
es casi idéntica a la que escribiste (aunque en Java vs C #). ElVeracodeServiceImplTest
es en realidad una clase de prueba de unidad. Los@Injectable
campos son esencialmente objetos simulados que se insertan en el contexto. El@Tested
campo es el objeto / clase con el DI Constructor definido. Estoy de acuerdo con su punto de vista que prefiere la inyección de Constructor sobre la inyección de campo. Sin embargo, como mencioné, el autor de JMockit siente lo contrario y estoy tratando de entender por quéEl argumento de menos inicialización de prueba es válido, pero hay otras preocupaciones que deben tenerse en cuenta. En primer lugar, debes responder una pregunta:
¿Quiero que mi clase sea instanciable solo con reflexión?
El uso de inyecciones de campo significa reducir la compatibilidad de una clase a entornos de inyección de dependencia que crean instancias de objetos utilizando la reflexión y admiten estas anotaciones de inyección particulares. Algunas plataformas basadas en el lenguaje Java ni siquiera admiten la reflexión ( GWT ), por lo que la clase inyectada en campo no será compatible con ellas.
El segundo problema es el rendimiento . Una llamada de constructor (directa o por reflexión) siempre es más rápida que un montón de asignaciones de campos de reflexión. Los marcos de inyección de dependencias deben usar el análisis de reflexión para construir un árbol de dependencia y crear constructores de reflexión. Este proceso provoca un impacto adicional en el rendimiento.
El rendimiento afecta la mantenibilidad . Si cada conjunto de pruebas debe ejecutarse en algún tipo de contenedor de inyección de dependencia, una ejecución de prueba de unos pocos miles de pruebas unitarias puede durar decenas de minutos. Dependiendo del tamaño de una base de código, esto puede ser un problema.
Todo esto plantea muchas preguntas nuevas:
En general, cuanto más grande y más importante es un proyecto, más importantes son estos factores. Además, en términos de calidad, generalmente nos gustaría mantener el código alto en compatibilidad, comprobabilidad y mantenibilidad. Desde el punto de vista filosófico, la inyección de campo rompe la encapsulación , que es uno de los cuatro fundamentos de la programación orientada a objetos , que es el paradigma principal de Java.
Muchos, muchos argumentos en contra de la inyección de campo.
fuente
La inyección de campo obtiene un voto definitivo de "No" de mi parte.
Al igual que Robert Harvey, es demasiado automático para mi gusto. Prefiero el código explícito sobre el implícito, y tolero la indirección solo cuando / cuando proporciona beneficios claros, ya que hace que el código sea más difícil de entender y razonar.
Al igual que Maciej Chałapuk, no me gusta necesitar reflexión o un marco DI / IoC para crear una instancia de una clase. Es como conducir a Roma para llegar a París desde Ámsterdam.
Eso sí, me encanta DI y todas las ventajas que trae. Simplemente no estoy seguro de necesitar los marcos para crear una instancia de una clase. Especialmente no el efecto que los contenedores IoC u otros marcos DI pueden tener en el código de prueba.
Me gusta que mis pruebas sean muy sencillas. No me gusta tener que pasar por la indirecta de configurar un contenedor IoC u otro marco de trabajo DI para luego configurar mi clase bajo prueba. Simplemente se siente incómodo.
Y hace que mis pruebas dependan del estado global del marco. Es decir, ya no puedo ejecutar dos pruebas en paralelo que requieren que se configure una instancia de clase X con diferentes dependencias.
Al menos con la inyección del constructor y del instalador, tiene la opción de no usar los marcos y / o la reflexión en sus pruebas.
fuente
Bueno, esto parece una discusión polémica. Lo primero que debe abordarse es que la inyección de campo es diferente de la inyección de Setter . Ya trabajo con algunas personas que piensan que la inyección Field and Setter es lo mismo.
Entonces, para ser claros:
Inyección de campo:
Setter inyección:
Pero, para mí, comparten las mismas preocupaciones. Y, mi favorito, la inyección del constructor:
Tengo las siguientes razones para creer que la inyección del constructor es mejor que la inyección de setter / field (citaré algunos enlaces de Spring para demostrar mi punto):
@AutoWired
difusión entre su código, su código depende menos del marco. Sé que la posibilidad de cambiar simplemente el marco DI en un proyecto no justifica eso (¿quién lo hace, verdad?). Pero esto muestra, para mí, un código mejor (aprendí esto de una manera difícil con EJB y búsquedas). Y con Spring incluso puede eliminar la@AutoWired
anotación utilizando la inyección del constructor.Si está buscando más información, le recomiendo este artículo antiguo (pero aún relevante) de Spring Blog que nos dice por qué usan tanta inyección de setter y la recomendación de usar la inyección de constructor:
Y esta nota sobre por qué creen que el constructor es más adecuado para el código de la aplicación:
Pensamientos finales
Si no está realmente seguro de la ventaja del constructor, tal vez la idea de mezclar setter (no campo) con inyección de constructor es una opción, como lo explicó el equipo de Spring :
Pero recuerde que la inyección de campo es, probablemente, la que debe evitarse entre los tres.
fuente
¿Es probable que su dependencia cambie durante la vida de la clase? Si es así, use la inyección de campo / propiedad. De lo contrario, use la inyección del constructor.
fuente