inversion-of-control - register - ioc informatica
Constructor por defecto versus contenedor IOC (8)
Además del acoplamiento flexible, un IoC reducirá la duplicación de código. Cuando utiliza un IoC y desea cambiar la implementación predeterminada de su interfaz, debe cambiarlo en un solo lugar. Cuando utiliza contructors predeterminados para inyectar la implementación predeterminada, debe cambiarla en todos los lugares donde se usa la interfaz.
¿Puede alguien explicarme las ventajas de usar un contenedor IOC sobre simplemente codificar la implementación predeterminada en un constructor predeterminado?
En otras palabras, ¿qué hay de malo con este código?
public class MyClass
{
private IMyInterface _myInterface;
public MyClass()
{
_myInterface = new DefaultMyInterface();
}
public MyClass(IMyInterface myInterface)
{
_myInterface = myInterface;
}
}
Por lo que puedo decir, esta clase admite la inyección del constructor lo suficiente como para que las pruebas unitarias y burlas se hagan fácilmente. Además de eso, el constructor predeterminado elimina la carga computacional del contenedor IOC (sin mencionar que todo el proceso es mucho más transparente).
Los únicos beneficios que puedo ver al usar un contenedor IOC es si necesita cambiar la implementación de sus interfaces con frecuencia. ¿Me estoy perdiendo de algo?
Elige un bando :)
En resumen, se recomienda COI. El problema con el código es que no puedo cambiar la implementación predeterminada de la dependencia sin volver a compilar el código como lo ha indicado al final. IOC le permite cambiar la configuración o composición de su objeto en un archivo externo sin recompilación.
El COI asume la responsabilidad de "construcción y montaje" del resto del código. El propósito de IOC no es hacer que tu código sea comprobable ... es un efecto secundario agradable. (Al igual que el código TDDed conduce a un mejor diseño)
No hay nada de malo en este código y todavía puede usarlo con marcos de Inyección de Dependencia como Spring y Guice.
Muchos desarrolladores ven el archivo de configuración XML de Spring como una ventaja sobre las dependencias de cableado dentro del código, ya que puede cambiar las implementaciones sin necesidad de un paso de compilación. Este beneficio en realidad se realiza en situaciones donde ya tiene varias implementaciones compiladas en su ruta de clase y desea elegir la implementación en el momento del despliegue. Puede imaginar una situación en la que un componente es provisto por un tercero después de la implementación. Del mismo modo, puede haber un caso en el que desee enviar implementaciones adicionales como un parche después de la implementación.
Pero no todos los marcos DI usan la configuración XML. Google Guice, por ejemplo, tiene módulos escritos como clases de Java que deben compilarse como cualquier otra clase de Java.
Entonces, ¿cuál es la ventaja de DI si incluso necesitas un paso de compilación? Esto nos lleva de vuelta a tu pregunta original. Puedo ver las siguientes ventajas:
- Enfoque estándar de DI a lo largo de la aplicación.
- Configuración cuidadosamente separada de otra lógica.
- Posibilidad de inyectar proxies. Spring, por ejemplo, le permite realizar el manejo declarativo de transacciones mediante la inyección de proxies en lugar de su implementación
- Reutilización más fácil de la lógica de configuración. Cuando usa DI de manera extensiva, verá un complejo árbol de dependencias que evoluciona con el tiempo. Administrarlo sin una capa de configuración claramente separada y soporte de marco puede ser una pesadilla. Los marcos DI hacen que sea fácil reutilizar la lógica de configuración a través de la herencia y otros medios.
No veo por qué su técnica de codificación rígida de la implementación predeterminada no pudo ser utilizada junto con un contenedor IOC. Simplemente, las dependencias que no especifiques en la configuración tomarían la implementación predeterminada.
¿O me estoy perdiendo algo?
Una razón por la que quizás desee utilizar un Contenedor IOC sería facilitar la configuración tardía de su software.
Supongamos, por ejemplo, que proporciona varias implementaciones de una interfaz particular: el cliente (o su equipo de servicios profesionales) puede decidir cuál usar modificando el archivo de configuración de IOC.
Además de los otros comentarios, uno podría argumentar el principio DRY (No repetir) en estos casos. Es redundante tener que poner ese código de construcción predeterminado en cada clase. También presenta el manejo especial de casos donde no es necesario que exista ninguno.
La única preocupación que tengo es (1) si su dependencia de servicio predeterminada tiene otra dependencia de servicio / secundaria (y así sucesivamente ... su DefaultMyInterface depende de ILogger) y (2) necesita aislar la primera dependencia de servicio de la segunda ( necesita probar DefaultMyInterface con un stub ILogger). En ese caso, evidentemente necesitará perder la "nueva DefaultMyInterface" predeterminada y en su lugar hacer una de las siguientes: (1) inyección de dependencia pura o (2) localizador de servicio o (3) container.BuildUp (new DefaultMyInterface ());
Algunas de las preocupaciones que aparecen en otros carteles pueden no ser justas para su pregunta. No estabas preguntando acerca de múltiples implementaciones de "producción". Usted está preguntando sobre pruebas unitarias. En el caso de probar la unidad, su enfoque, con mi primera advertencia declarada, parece legítimo; Yo también consideraría usarlo en casos simples de prueba unitaria.
Del mismo modo, un par de personas que respondieron expresaron su preocupación por la repitencia. Tampoco me gusta la repetición, pero si (1) su implementación predeterminada es su predeterminada (YAGNI: no tiene planes de cambiar la predeterminada) y (2) no cree que la primera advertencia que establecí aplique y (3) prefiera el enfoque de código más simple que haya compartido, entonces no creo que esta forma particular de repitición sea un problema.
La idea de IoC es delegar parte de la funcionalidad de su componente en otra parte del sistema. En el mundo de IoC, tiene componentes que no se conocen entre sí. Su ejemplo infringe esto, ya que está creando un acoplamiento estricto entre MyClass y alguna implementación de IMyInterface. La idea principal es que su componente no tenga conocimiento sobre cómo se usará. En su ejemplo, su componente hace algunas suposiciones sobre su uso.
En realidad, este enfoque puede funcionar, pero mezclar IoC e inicialización explícita de objetos no es una buena práctica de OMI.
IoC le ofrece un acoplamiento flexible mediante la realización de vinculaciones finales por el precio de la claridad del código. Cuando agrega un comportamiento adicional a este proceso, hace las cosas aún más complicadas y puede dar lugar a errores cuando algunos componentes pueden recibir objetos con comportamientos no deseados o imprevistos.