xml - qué - ¿Cuáles son los beneficios de los contenedores de inyección de dependencia?
inyeccion de dependencias wpf (16)
Entiendo los beneficios de la inyección de dependencia en sí. Tomemos Spring por ejemplo. También entiendo los beneficios de otras características de Spring como AOP, ayudantes de diferentes tipos, etc. Me pregunto cuáles son los beneficios de la configuración XML, tales como:
<bean id="Mary" class="foo.bar.Female">
<property name="age" value="23"/>
</bean>
<bean id="John" class="foo.bar.Male">
<property name="girlfriend" ref="Mary"/>
</bean>
en comparación con el antiguo código Java simple, como:
Female mary = new Female();
mary.setAge(23);
Male john = new Male();
john.setGirlfriend(mary);
que es más fácil de eliminar, comprueba el tiempo de compilación y puede ser entendido por cualquier persona que solo conozca Java. Entonces, ¿cuál es el objetivo principal de un marco de inyección de dependencia? (o un pedazo de código que muestra sus beneficios).
ACTUALIZAR:
En caso de
IService myService;// ...
public void doSomething() {
myService.fetchData();
}
¿Cómo puede IoC framework adivinar qué implementación de myService quiero inyectar si hay más de una? Si solo hay una implementación de una interfaz dada, y dejo que el contenedor de IoC decida usarla automáticamente, se romperá después de que aparezca una segunda implementación. Y si intencionalmente solo hay una implementación posible de una interfaz, entonces no necesita inyectarla.
Sería realmente interesante ver una pequeña pieza de configuración para IoC que muestra sus beneficios. He estado usando Spring por un tiempo y no puedo dar ese ejemplo. Y puedo mostrar líneas individuales que demuestran los beneficios de Hibernate, Dwr y otros frameworks que uso.
ACTUALIZACIÓN 2:
Me doy cuenta de que la configuración de IoC se puede cambiar sin volver a compilar. ¿Es realmente una buena idea? Puedo entender cuando alguien quiere cambiar las credenciales de DB sin volver a compilar, puede que no sea desarrollador. En su práctica, ¿con qué frecuencia alguien más que no sea el desarrollador cambia la configuración de IoC? Creo que para los desarrolladores no hay ningún esfuerzo para recompilar esa clase en particular en lugar de cambiar la configuración. Y para los que no son desarrolladores, es probable que desee hacer su vida más fácil y proporcionar un archivo de configuración más simple.
ACTUALIZACIÓN 3:
Configuración externa de mapeo entre interfaces y sus implementaciones concretas
¿Qué hay de bueno en hacerlo extenso? No hace que todo su código sea externo, mientras que definitivamente puede: simplemente colóquelo en el archivo ClassName.java.txt, léalo y compílelo manualmente sobre la marcha: wow, evitó volver a compilar. ¿Por qué se debería evitar la compilación?
Ahorras tiempo de codificación porque proporcionas mapeos de manera declarativa, no en un código de procedimiento
Entiendo que a veces el enfoque declarativo ahorra tiempo. Por ejemplo, declaro solo una vez una asignación entre una propiedad de bean y una columna de DB e hibernate usa esta asignación al cargar, guardar, construir SQL basado en HSQL, etc. Aquí es donde funciona el enfoque declarativo. En el caso de Spring (en mi ejemplo), la declaración tenía más líneas y tenía la misma expresividad que el código correspondiente. Si hay un ejemplo cuando tal declaración es más corta que el código, me gustaría verla.
El principio de Inversión de control permite pruebas sencillas de unidades porque puede reemplazar las implementaciones reales por falsas (como reemplazar una base de datos SQL por una memoria)
Entiendo los beneficios de la inversión de control (prefiero llamar al patrón de diseño que se discute aquí como Inyección de dependencia, porque el IoC es más general; hay muchos tipos de control, y solo invertimos uno de ellos, el control de la inicialización). Estaba preguntando por qué alguien alguna vez necesita algo más que un lenguaje de programación para ello. Definitivamente puedo reemplazar implementaciones reales con falsificaciones usando código. Y este código expresará lo mismo que la configuración: simplemente inicializará los campos con valores falsos.
mary = new FakeFemale();
Entiendo los beneficios de DI. No entiendo qué beneficios se agregan mediante la configuración XML externa en comparación con la configuración del código que hace lo mismo. No creo que se deba evitar la compilación. Compilo todos los días y todavía estoy vivo. Creo que la configuración de DI es un mal ejemplo de enfoque declarativo. La declaración puede ser útil si se declara una vez Y se usa muchas veces de diferentes maneras, como hibernate cfg, donde la asignación entre propiedad bean y columna DB se usa para guardar, cargar, generar consultas de búsqueda, etc. La configuración DI primavera se puede traducir fácilmente a configurando el código, como en el comienzo de esta pregunta, ¿no es así? Y se usa solo para la inicialización del frijol, ¿no es así? Lo que significa que un enfoque declarativo no agrega nada aquí, ¿verdad?
Cuando declaro el mapeo de hibernación, solo doy algo de hibernación a la información, y funciona en función de eso; no le digo qué hacer. En caso de primavera, mi declaración le dice a la primavera exactamente qué hacer, entonces, ¿por qué declararlo? ¿Por qué no hacerlo?
ÚLTIMA ACTUALIZACIÓN:
Chicos, muchas respuestas me dicen sobre la inyección de dependencia, que SÉ ES BUENO. La pregunta es sobre el propósito de la configuración de DI en lugar de inicializar el código: tiendo a pensar que el código de inicialización es más corto y más claro. La única respuesta que obtuve hasta ahora a mi pregunta es que evita la recompilación cuando cambia la configuración. Supongo que debería publicar otra pregunta, porque es un gran secreto para mí, por qué debería evitarse la compilación en este caso.
A menudo, el punto importante es quién está cambiando la configuración después de que se escribió el programa. Con la configuración en el código, suponemos implícitamente que la persona que lo cambia tiene las mismas habilidades y el mismo acceso al código fuente, etc., que el autor original.
En los sistemas de producción es muy práctico extraer un subconjunto de configuraciones (por ejemplo, la edad en su ejemplo) a un archivo XML y permitir que el administrador del sistema o el personal de soporte cambien el valor sin otorgarles el poder completo sobre el código fuente u otras configuraciones. para aislarlos de las complejidades.
Cada vez que puede cambiar su código a datos, está dando un paso en la dirección correcta.
Codificar algo como datos significa que su código en sí mismo es más general y reutilizable. También significa que sus datos pueden especificarse en un idioma que se ajuste exactamente.
Además, un archivo XML se puede leer en una GUI u otra herramienta y se puede manipular fácilmente de forma pragmática. ¿Cómo harías eso con el ejemplo del código?
Estoy constantemente factorizando cosas que la mayoría de la gente implementaría como código en datos, hace que el código quede MUCHO más limpio. Me resulta inconcebible que las personas creen un menú en código en lugar de datos: debería ser obvio que hacerlo en código es simplemente incorrecto debido a la repetición.
Desde una perspectiva de primavera, puedo darte dos respuestas.
Primero, la configuración XML no es la única forma de definir la configuración. La mayoría de las cosas se pueden configurar mediante anotaciones y las cosas que se deben hacer con XML son la configuración del código que no está escribiendo de todos modos, como un grupo de conexiones que está utilizando desde una biblioteca. Spring 3 incluye un método para definir la configuración DI utilizando Java similar a la configuración DI rodada a mano en su ejemplo. Entonces, usar Spring no significa que tenga que usar un archivo de configuración basado en XML.
En segundo lugar, Spring es mucho más que solo un marco DI. Tiene muchas otras características, incluida la gestión de transacciones y el AOP. La configuración Spring XML mezcla todos estos conceptos. Often in the same configuration file I''m specifying bean dependencies, transaction settings and adding session scoped beans that actually handled using AOP in the background. I find the XML configuration provides a better place to manage all these features. I also feel that the annotation based configuration and XML configuration scale up better than doing Java based configuration.
But I do see your point and there isn''t anything wrong with defining the dependency injection configuration in Java. I normally do that myself in unit tests and when I''m working on a project small enough that I haven''t added a DI framework. I don''t normally specify configuration in Java because to me that''s the kind plumbing code that I''m trying to get away from writing when I chose to use Spring. That''s a preference though, it doesn''t mean that XML configuration is superior to Java based configuration.
En el mundo de .NET, la mayoría de los marcos de IoC proporcionan configuración de código y XML.
StructureMap y Ninject, por ejemplo, usan interfaces fluidas para configurar contenedores. Ya no está obligado a usar archivos de configuración XML. Spring, que también existe en .NET, depende en gran medida de los archivos XML, ya que es su interfaz de configuración principal histórica, pero aún es posible configurar contenedores mediante programación.
Esta es una pregunta un poco cargada, pero tiendo a estar de acuerdo en que grandes cantidades de configuración xml realmente no significan mucho beneficio. Me gusta que mis aplicaciones sean tan livianas en cuanto a dependencias como sea posible, incluidos los marcos robustos.
Simplifican el código muchas veces, pero también tienen una sobrecarga en la complejidad que hace que rastrear los problemas sea bastante difícil (he visto tales problemas de primera mano, y con Java directamente sería mucho más cómodo tratar con ellos).
Supongo que depende un poco del estilo y con qué te sientes cómodo ... te gusta volar tu propia solución y tener la ventaja de saberlo de cerca, o recurrir a las soluciones existentes que pueden resultar difíciles cuando la configuración no es así. t justo? Es todo un intercambio.
Sin embargo, la configuración XML es un poco molesta ... Trato de evitarla a toda costa.
Facilidad para combinar configuraciones parciales en una configuración final completa.
Por ejemplo, en las aplicaciones web, el modelo, la vista y los controladores normalmente se especifican en archivos de configuración separados. Use el enfoque declarativo, puede cargar, por ejemplo:
UI-context.xml Model-context.xml Controller-context.xml
O cargue con una interfaz de usuario diferente y algunos controladores adicionales:
AlternateUI-context.xml Model-context.xml Controller-context.xml ControllerAdditions-context.xml
Hacer lo mismo en el código requiere una infraestructura para combinar configuraciones parciales. No es imposible hacerlo en código, pero ciertamente es más fácil hacerlo usando un framework IoC.
La inyección de dependencia es un estilo de codificación que tiene sus raíces en la observación de que la delegación de objetos suele ser un patrón de diseño más útil que la herencia de objetos (es decir, el objeto tiene una relación más útil que el objeto). Sin embargo, es necesario otro ingrediente para que la DI funcione, la de crear interfaces de objeto. Combinando estos dos potentes patrones de diseño, los ingenieros de software se dieron cuenta rápidamente de que podían crear un código flexible y flexible, y así nació el concepto de Dependency Injection. Sin embargo, no fue hasta que la reflexión de objetos estuvo disponible en ciertos lenguajes de alto nivel que DI realmente despegó. El componente de reflexión es fundamental para la mayoría de los sistemas actuales de DI porque los aspectos realmente interesantes de DI requieren la capacidad de seleccionar objetos por programa y configurarlos e insertarlos en otros objetos utilizando un sistema externo e independiente a los objetos mismos.
Un lenguaje debe proporcionar un buen soporte tanto para las técnicas de programación orientadas a objetos normales como para la compatibilidad con las interfaces de objetos y la reflexión de objetos (por ejemplo, Java y C #). Si bien puede crear programas utilizando patrones DI en sistemas C ++, su falta de soporte de reflexión dentro del lenguaje apropiado le impide admitir servidores de aplicaciones y otras plataformas DI y, por lo tanto, limita la expresividad de los patrones DI.
Fortalezas de un sistema construido usando patrones DI:
- El código DI es mucho más fácil de reutilizar ya que la funcionalidad ''dependiente'' se extrapola en interfaces bien definidas, permitiendo objetos separados cuya configuración es manejada por una plataforma de aplicación adecuada para ser conectada a otros objetos a voluntad.
- El código DI es mucho más fácil de probar. La funcionalidad expresada por el objeto se puede probar en un recuadro negro construyendo objetos ''simulados'' que implementan las interfaces esperadas por la lógica de su aplicación.
- El código DI es más flexible. Es un código innatamente débilmente acoplado, hasta el extremo. Esto le permite al programador escoger y elegir cómo se conectan los objetos basándose exclusivamente en sus interfaces requeridas en un extremo y sus interfaces expresadas en el otro.
- La configuración externa (Xml) de objetos DI significa que otros pueden personalizar su código en direcciones imprevistas.
- La configuración externa también es una separación del patrón de preocupación en el sentido de que todos los problemas de inicialización de objetos y administración de interdependencia de objetos pueden ser manejados por el servidor de aplicaciones.
- Tenga en cuenta que no se requiere una configuración externa para usar el patrón DI, para las interconexiones simples, un objeto pequeño constructor suele ser adecuado. Hay una compensación de flexibilidad entre los dos. Un objeto de generador no es una opción tan flexible como un archivo de configuración externamente visible. El desarrollador del sistema DI debe sopesar las ventajas de flexibilidad sobre la conveniencia, teniendo cuidado de que el control a pequeña escala y de grano fino sobre la construcción del objeto expresado en un archivo de configuración pueda aumentar la confusión y los costos de mantenimiento más adelante.
Definitivamente, el código DI parece más engorroso, las desventajas de tener todos esos archivos XML que configuran objetos para ser inyectados en otros objetos parecen difíciles. Este es, sin embargo, el punto de los sistemas DI. Su capacidad para mezclar y combinar objetos de código como una serie de configuraciones le permite construir sistemas complejos usando código de terceros con una codificación mínima de su parte.
El ejemplo provisto en la pregunta simplemente toca en la superficie del poder expresivo que puede proporcionar una biblioteca de objetos DI debidamente factorizada. Con un poco de práctica y mucha autodisciplina, la mayoría de los profesionales de DI descubren que pueden construir sistemas que tienen una cobertura de prueba del 100% del código de la aplicación. Este único punto es extraordinario. Esta no es una cobertura del 100% de prueba de una aplicación pequeña de unos pocos cientos de líneas de código, sino una cobertura de prueba del 100% de aplicaciones que comprenden cientos de miles de líneas de código. No puedo describir ningún otro patrón de diseño que brinde este nivel de capacidad de prueba.
Tiene razón en que una aplicación de apenas 10s de líneas de código es más fácil de entender que varios objetos más una serie de archivos de configuración XML. Sin embargo, como con los patrones de diseño más potentes, las ganancias se encuentran a medida que continúa agregando nuevas características al sistema.
En resumen, las aplicaciones basadas en DI a gran escala son más fáciles de depurar y más fáciles de entender. Si bien la configuración Xml no es "tiempo de compilación verificado", todos los servicios de aplicaciones de los que este autor tenga conocimiento proporcionarán al desarrollador mensajes de error si intentan inyectar un objeto que tiene una interfaz incompatible con otro objeto. Y la mayoría proporciona una función de ''verificación'' que cubre todas las configuraciones de objetos conocidos. Esto se realiza fácil y rápidamente comprobando que el objeto A que se va a inyectar implementa la interfaz requerida por el objeto B para todas las inyecciones de objetos configurados.
La razón para usar un contenedor DI es que no tiene que tener un billón de propiedades preconfiguradas en su código que son simplemente getters y setters. ¿De verdad quieres codificar todos aquellos con la nueva X ()? Claro, puede tener un valor por defecto, pero el contenedor DI permite la creación de singletons que es extremadamente fácil y le permite enfocarse en los detalles del código, no en la tarea miscelánea de inicializarlo.
Por ejemplo, Spring le permite implementar la interfaz InitializingBean y agregar un método afterPropertiesSet (también puede especificar un "método init" para evitar acoplar su código a Spring). Estos métodos le permitirán asegurarse de que cualquier interfaz especificada como un campo en su instancia de clase esté configurada correctamente al inicio, y luego ya no tendrá que anular la verificación de sus getters y setters (suponiendo que permita que sus singletons permanezcan seguros para subprocesos) )
Además, es mucho más fácil realizar inicializaciones complejas con un contenedor DI en lugar de hacerlo usted mismo. Por ejemplo, ayudo con el uso de XFire (no de CeltiXFire, solo usamos Java 1.4). La aplicación utilizó Spring, pero desafortunadamente usó el mecanismo de configuración services.xml de XFire. Cuando una Colección de elementos necesitaba declarar que tenía CERO o más instancias en lugar de UNA o más instancias, tuve que anular parte del código XFire proporcionado para este servicio en particular.
Hay ciertos valores predeterminados de XFire definidos en su esquema Spring beans. Por lo tanto, si utilizáramos Spring para configurar los servicios, los beans podrían haberse utilizado. En cambio, lo que sucedió fue que tuve que proporcionar una instancia de una clase específica en el archivo services.xml en lugar de usar los beans. Para hacer esto, necesitaba proporcionar el constructor y configurar las referencias declaradas en la configuración de XFire. El verdadero cambio que necesitaba hacer requería que sobrecargara una sola clase.
Pero, gracias al archivo services.xml, tuve que crear cuatro nuevas clases, estableciendo sus valores predeterminados de acuerdo con sus valores predeterminados en los archivos de configuración de Spring en sus constructores. Si hubiéramos podido usar la configuración de Spring, podría haber dicho:
<bean id="base" parent="RootXFireBean">
<property name="secondProperty" ref="secondBean" />
</bean>
<bean id="secondBean" parent="secondaryXFireBean">
<property name="firstProperty" ref="thirdBean" />
</bean>
<bean id="thirdBean" parent="thirdXFireBean">
<property name="secondProperty" ref="myNewBean" />
</bean>
<bean id="myNewBean" class="WowItsActuallyTheCodeThatChanged" />
En cambio, se veía más como esto:
public class TheFirstPointlessClass extends SomeXFireClass {
public TheFirstPointlessClass() {
setFirstProperty(new TheSecondPointlessClass());
setSecondProperty(new TheThingThatWasHereBefore());
}
}
public class TheSecondPointlessClass extends YetAnotherXFireClass {
public TheSecondPointlessClass() {
setFirstProperty(TheThirdPointlessClass());
}
}
public class TheThirdPointlessClass extends GeeAnotherXFireClass {
public TheThirdPointlessClass() {
setFirstProperty(new AnotherThingThatWasHereBefore());
setSecondProperty(new WowItsActuallyTheCodeThatChanged());
}
}
public class WowItsActuallyTheCodeThatChanged extends TheXFireClassIActuallyCareAbout {
public WowItsActuallyTheCodeThatChanged() {
}
public overrideTheMethod(Object[] arguments) {
//Do overridden stuff
}
}
Por lo tanto, el resultado neto es que se tuvieron que agregar cuatro clases de Java adicionales, la mayoría inútiles, a la base de código para lograr el efecto de lograr una clase adicional y una información simple de contenedor de dependencia. Esta no es la "excepción que demuestra la regla", esta es la regla ... manejar las peculiaridades en el código es mucho más claro cuando las propiedades ya están provistas en un contenedor DI y simplemente las está cambiando para adaptarse a una situación especial, que sucede más a menudo que no.
No necesita volver a compilar su código cada vez que cambie algo en la configuración. Simplificará la implementación y el mantenimiento del programa. Por ejemplo, puede intercambiar un componente por otro con solo 1 cambio en el archivo de configuración.
Para mí, una de las razones principales para usar un IoC (y hacer uso de una configuración externa) es en torno a las dos áreas de:
- Pruebas
- Mantenimiento de producción
Pruebas
Si divide su prueba en 3 escenarios (que es bastante normal en el desarrollo a gran escala):
- Examen de la unidad
- Pruebas de integración
- Prueba de caja negra
Lo que querrá hacer es para los dos últimos escenarios de prueba (Integration & Black box), no es recompilar ninguna parte de la aplicación.
Si alguno de los escenarios de prueba requiere que cambie la configuración (es decir, use otro componente para imitar una integración bancaria, o realice una carga de rendimiento), esto puede manejarse fácilmente (esto tiene los beneficios de configurar el lado DI de un IoC sin embargo.
Además, si su aplicación se usa en múltiples sitios (con diferente configuración de servidor y componente) o tiene una configuración cambiante en el entorno en vivo, puede usar las últimas etapas de prueba para verificar que la aplicación manejará esos cambios.
Producción
Como desarrollador, usted no (y no debería) tener el control del entorno de producción (en particular cuando su aplicación se distribuye a múltiples clientes o sitios separados), esto para mí es el beneficio real de usar tanto una configuración IoC como externa , ya que depende del soporte de infraestructura / producción ajustar y ajustar el entorno en vivo sin tener que volver a los desarrolladores y a través de la prueba (mayor costo cuando lo único que quieren hacer es mover un componente).
Resumen
Los principales beneficios de que la configuración externa de una IoC provenga de darle a otros (no desarrolladores) la capacidad de configurar su aplicación, en mi experiencia esto solo es útil bajo un conjunto limitado de circunstancias:
- La aplicación se distribuye a múltiples sitios / clientes donde los ambientes serán diferentes.
- Control / entrada de desarrollo limitado sobre el entorno de producción y la configuración.
- Escenarios de prueba.
En la práctica, descubrí que incluso cuando se desarrolla algo con el que se tiene control sobre el entorno, se ejecuta, con el tiempo es mejor otorgar a alguien más las capacidades para cambiar la configuración:
- Al desarrollar, no sabes cuándo cambiará (la aplicación es tan útil que tu empresa se la vende a otra persona).
- No quiero tener que cambiar el código cada vez que se solicita un cambio leve que podría haber sido manejado configurando y usando un buen modelo de configuración.
Nota: la aplicación se refiere a la solución completa (no solo al ejecutable), por lo que todos los archivos requieren que la aplicación se ejecute .
Puede insertar una nueva implementación para su novia. Entonces se puede inyectar nueva hembra sin recompilar el código.
<bean id="jane" class="foo.bar.HotFemale">
<property name="age" value="19"/>
</bean>
<bean id="mary" class="foo.bar.Female">
<property name="age" value="23"/>
</bean>
<bean id="john" class="foo.bar.Male">
<property name="girlfriend" ref="jane"/>
</bean>
(Lo anterior supone que Female y HotFemale implementan la misma interfaz de GirlfFriend)
Tengo tu respuesta
Obviamente, hay intercambios en cada enfoque, pero los archivos de configuración XML externalizados son útiles para el desarrollo empresarial en el que los sistemas de compilación se utilizan para compilar el código y no su IDE. Usando el sistema de compilación, es posible que desee inyectar ciertos valores en su código, por ejemplo, la versión de la compilación (lo que puede ser doloroso tener que actualizar manualmente cada vez que compila). El dolor es mayor cuando su sistema de compilación extrae el código de algún sistema de control de versiones. La modificación de valores simples en el momento de la compilación requeriría que usted cambie un archivo, lo comprometa, compile, y luego revierta cada vez para cada cambio. Estos no son cambios que quiera comprometer con el control de su versión.
Otros casos de uso útiles relacionados con el sistema de compilación y las configuraciones externas:
- Inyección de estilos / hojas de estilo para una única base de código para diferentes compilaciones
- inyectando diferentes conjuntos de contenido dinámico (o referencias a ellos) para su base de código único
- Inyectar contexto de localización para diferentes compilaciones / clientes
- cambiar un URI de servicio web a un servidor de respaldo (cuando el principal se cae)
Actualización: todos los ejemplos anteriores se refieren a cosas que no necesariamente requieren dependencias en las clases. Pero puede crear fácilmente casos donde tanto un objeto complejo como la automatización son necesarios, por ejemplo:
- Imagine que tiene un sistema en el que monitorea el tráfico de su sitio web. Dependiendo del número de usuarios concurrentes, enciende / apaga un mecanismo de registro. Quizás mientras el mecanismo está apagado, se coloca un objeto auxiliar en su lugar.
- Imagine que tiene un sistema de conferencia web en el que, dependiendo del número de usuarios, desea cambiar la capacidad de hacer P2P según el número de participantes.
Initializing in an XML config file will simplify your debugging / adapting work with a client who has your app deployed on their computers. (Because it doesn''t require recompilation + binary files replacement)
One of the most appealing reasons is the " Hollywood principle ": don''t call us, we''ll call you. A component is not required to do the lookups to other components and services itself; instead they are provided to it automatically. In Java, this means that it is no longer necessary to do JNDI lookups inside the component.
It is also lots easier to unit test a component in isolation: instead of giving it an actual implementation of the components it needs, you simply use (possibly auto generated) mocks.
Spring also has a properties loader. We use this method to set variables that are dependant on the environment (eg development, testing, acceptance, production, ...). This could be for example the queue to listen to.
If there is no reason why the property would change, there is also no reason to configure it in this way.
Your case is very simple and therefore doesn''t need an IoC (Inversion of Control) container like Spring. On the other hand, when you "program to interfaces, not implementations" (which is a good practice in OOP), you can have code like this:
IService myService;
// ...
public void doSomething() {
myService.fetchData();
}
(note that the type of myService is IService -- an interface, not a concrete implementation). Now it can be handy to let your IoC container automatically provide the correct concrete instance of IService during initialization - when you have many interfaces and many implementations, it can be cumbersome to do that by hand. Main benefits of an IoC container (dependency injection framework) are:
- External configuration of mapping between interfaces and their concrete implementations
- IoC container handles some tricky issues like resolving complicated dependency graphs, managing component''s lifetime etc.
- You save coding time because you provide mappings declaratively, not in a procedural code
- Inversion of Control principle allows for easy unit testing because you can replace real implementations with fake ones (like replacing SQL database with an in-memory one)