Soy nuevo en la inyección de dependencias y tengo algunas preguntas sobre qué estilo debo usar en mis aplicaciones. Acabo de leer Inversion of Control Containers y el patrón de Inyección de dependencias de Martin Fowler, pero no puedo obtener la diferencia práctica entre el constructor, el setter y la inyección de interfaz.
Me parece que las razones para usar uno sobre el otro son solo una cuestión de limpieza y / o claridad del código. ¿Cuál es la diferencia? ¿Existen fuertes ventajas o desventajas de usar uno sobre el otro, o es solo lo que he dicho antes?
En mi opinión, la inyección de constructor es la más intuitiva de todas, así como la inyección de interfaz es la menos. Por otro lado, la inyección de setter es un término medio, pero ¿se supone que puedes cambiar la instancia del objeto de dependencia que inyectaste inicialmente? ¿Este estilo de inyección garantiza que el objeto que necesita la dependencia siempre lo tendrá inyectado? Creo que no, pero corrígeme si me equivoco.
Respuestas:
La inyección de constructor tiene la ventaja de que hace explícita la dependencia y obliga al cliente a proporcionar una instancia. También puede garantizar que el cliente no pueda cambiar la instancia más tarde. Una desventaja (posible) es que debe agregar un parámetro a su constructor.
Setter Injection tiene la ventaja de que no requiere agregar un parámetro al constructor. Tampoco requiere que el cliente configure la instancia. Esto es útil para dependencias opcionales. Esto también puede ser útil si desea que la clase cree, por ejemplo, un repositorio de datos real de forma predeterminada, y luego, en una prueba, puede usar el configurador para reemplazarlo con una instancia de prueba.
La inyección de interfaz , por lo que puedo decir, no es muy diferente a la inyección de setter. En ambos casos, está (opcionalmente) configurando una dependencia que se puede cambiar más adelante.
En última instancia, es una cuestión de preferencia y si se requiere o no una dependencia . Personalmente, uso la inyección de constructor casi exclusivamente. Me gusta que explique las dependencias de una clase al obligar al cliente a proporcionar una instancia en el constructor. También me gusta que el cliente no pueda cambiar la instancia después del hecho.
Muchas veces, mi única razón para pasar dos implementaciones separadas es para probar. En producción, puedo pasar a
DataRepository
, pero en pruebas, pasaría aFakeDataRepository
. En este caso, generalmente proporcionaré dos constructores: uno sin parámetros y otro que acepta aIDataRepository
. Luego, en el constructor sin parámetros, encadenaré una llamada al segundo constructor y pasaré anew DataRepository()
.Aquí hay un ejemplo en C #:
Esto se conoce como inyección de dependencia del pobre. Me gusta porque en el código del cliente de producción, no necesito repetirme al tener varias declaraciones repetidas que parecen
Sin embargo, todavía puedo pasar una implementación alternativa para las pruebas. Me doy cuenta de que con la DI de Poor Man estoy codificando mi dependencia, pero eso es aceptable para mí, ya que utilizo principalmente DI para las pruebas.fuente
Las diferencias entre la inyección del constructor y del setter ya se describieron adecuadamente anteriormente, por lo que no profundizaré más en ellas.
La inyección de interfaz es una forma de inyección más avanzada que es útil porque permite que la dependencia se decida en el momento en que se usa en lugar de durante la inicialización del objeto que lo usará. Esto permite una serie de opciones útiles:
La dependencia podría tener un alcance diferente al objeto en el que se inyecta; por ejemplo, puede usar la inyección de interfaz para proporcionar un objeto para el cual existe por sesión de usuario o por hilo a un singleton global. Cada vez que el objeto necesita la dependencia, llamará al método getter proporcionado por el marco, y esto puede devolver resultados diferentes dependiendo de la situación en la que se llame.
Permite una inicialización diferida: no es necesario que la dependencia se inicialice hasta que esté a punto de usarse
Permite que las dependencias se carguen desde una copia almacenada en caché cuando existen o se reinicializan cuando no existen (por ejemplo, utilizando a
SoftReference
en Java).Obviamente, las técnicas avanzadas como esta tienen desventajas; en este caso, el problema principal es que el código se vuelve menos claro (las clases utilizadas en su código se vuelven abstractas, y no existe una implementación concreta obvia de ellas, lo que puede ser confuso si no está acostumbrado) y se vuelve más dependiente en su marco de inyección de dependencia (todavía es posible instanciar sus objetos manualmente, por supuesto, pero es más difícil que con otros estilos de inyección).
fuente