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 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 depurar, se comprueba el tiempo de compilación y puede ser entendido por cualquiera que sólo conozca Java. Entonces, ¿cuál es el propósito principal de un marco de inyección de dependencia? (o un fragmento de código que muestre sus beneficios).
ACTUALIZACIÓN:
En caso de
IService myService;// ...
public void doSomething() {
myService.fetchData();
}
¿Cómo puede el marco de IoC adivinar qué implementación de myService quiero inyectar si hay más de una? Si solo hay una implementación de la 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 posible implementación de una interfaz, entonces no es necesario inyectarla.
Sería realmente interesante ver una pequeña parte de la configuración para IoC que muestra sus beneficios. He estado usando Spring por un tiempo y no puedo proporcionar tal ejemplo. Y puedo mostrar líneas simples que demuestran los beneficios de hibernate, dwr y otros marcos que uso.
ACTUALIZACIÓN 2:
Me doy cuenta de que la configuración de IoC se puede cambiar sin volver a compilar. ¿Es realmente tan buena idea? Puedo entender cuando alguien quiere cambiar las credenciales de la base de datos sin volver a compilar; puede que no sea un 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 por recompilar esa clase en particular en lugar de cambiar la configuración. Y para los que no son desarrolladores, probablemente querrá facilitarles la vida 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é tiene de bueno hacerla extenuante? No hace que todo su código sea externo, aunque definitivamente puede hacerlo, solo colóquelo en el archivo ClassName.java.txt, lea y compile manualmente sobre la marcha, wow, evitó volver a compilar. ¿Por qué debería evitarse la compilación?
Ahorra tiempo de codificación porque proporciona asignaciones de forma declarativa, no en un código de procedimiento
Entiendo que a veces el enfoque declarativo ahorra tiempo. Por ejemplo, declaro solo una vez un mapeo entre una propiedad de bean y una columna de base de datos e hibernate usa este mapeo mientras carga, guarda, construye 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 en el que dicha declaración es más corta que el código, me gustaría verlo.
El principio de inversión de control permite una prueba unitaria sencilla porque puede reemplazar implementaciones reales con falsas (como reemplazar la base de datos SQL por una en memoria)
Entiendo los beneficios de la inversión de control (prefiero llamar al patrón de diseño discutido aquí como Inyección de Dependencia, porque IoC es más general; hay muchos tipos de control y solo estamos invirtiendo uno de ellos: el control de inicialización). Estaba preguntando por qué alguien necesita algo más que un lenguaje de programación para ello. Definitivamente puedo reemplazar implementaciones reales con falsas usando código. Y este código expresará lo mismo que la configuración: solo inicializará campos con valores falsos.
mary = new FakeFemale();
Entiendo los beneficios de DI. No entiendo qué beneficios agrega la configuración XML externa en comparación con la configuración de código que hace lo mismo. No creo que deba evitarse la compilación; compilo todos los días y sigo 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 el mapeo entre la propiedad del bean y la columna DB se usa para guardar, cargar, construir consultas de búsqueda, etc. La configuración de Spring DI se puede traducir fácilmente a configurando el código, como al principio de esta pregunta, ¿no es así? Y se usa solo para la inicialización del bean, ¿no es así? Lo que significa que un enfoque declarativo no agrega nada aquí, ¿verdad?
Cuando declaro el mapeo de hibernación, solo le doy a hibernación algo de información, y funciona en función de ella, 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 simplemente hacerlo?
ÚLTIMA ACTUALIZACIÓN:
Chicos, muchas respuestas me dicen sobre la inyección de dependencia, lo cual SÉ QUE ES BUENO. La pregunta es sobre el propósito de la configuración DI en lugar de inicializar el código; tiendo a pensar que la inicialización del código es más corta y clara. La única respuesta que he obtenido 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.
fuente
Respuestas:
Para mí, una de las principales razones para usar un IoC (y hacer uso de una configuración externa) es alrededor de las dos áreas de:
Pruebas
Si divide sus pruebas en 3 escenarios (lo cual es bastante normal en el desarrollo a gran escala):
Lo que querrá hacer es para los dos últimos escenarios de prueba (Integración y caja negra), no volver a compilar ninguna parte de la aplicación.
Si alguno de sus escenarios de prueba requiere que cambie la configuración (es decir, utilizar otro componente para imitar una integración bancaria o hacer una carga de rendimiento), esto se puede manejar fácilmente (esto se incluye en los beneficios de configurar el lado DI de un Sin embargo, IoC.
Además, si su aplicación se usa en varios sitios (con diferentes configuraciones de servidor y componentes) 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 tiene (y no debería) tener el control del entorno de producción (en particular cuando su aplicación se distribuye a varios clientes o sitios separados), para mí, este es el beneficio real de usar tanto una configuración de IoC como externa , ya que depende del soporte de infraestructura / producción modificar y ajustar el entorno en vivo sin tener que volver a los desarrolladores y realizar pruebas (costo más alto cuando todo lo que quieren hacer es mover un componente).
Resumen
Los principales beneficios de la configuración externa de un IoC provienen de brindar a otros (no desarrolladores) el poder de configurar su aplicación, en mi experiencia, esto solo es útil en un conjunto limitado de circunstancias:
En la práctica, descubrí que incluso al desarrollar algo sobre el que tienes control sobre el entorno en el que se ejecutará, con el tiempo es mejor darle a otra persona las capacidades para cambiar la configuración:
Nota: Aplicación se refiere a la solución completa (no solo al ejecutable), por lo que todos los archivos necesarios para que la aplicación se ejecute .
fuente
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 es más útil que el objeto es una relación). Sin embargo, para que DI funcione, es necesario otro ingrediente, el de crear interfaces de objetos. Combinando estos dos potentes patrones de diseño, los ingenieros de software se dieron cuenta rápidamente de que podían crear código flexible y débilmente acoplado y así nació el concepto de inyección de dependencia. 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 parte de la actualidad '
Un lenguaje debe proporcionar un buen soporte tanto para las técnicas normales de programación orientada a objetos como para las interfaces de objetos y la reflexión de objetos (por ejemplo, Java y C #). Si bien puede crear programas usando patrones DI en sistemas C ++, su falta de soporte de reflexión dentro del lenguaje propiamente dicho 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:
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 objetivo de los sistemas DI. Su capacidad para mezclar y combinar objetos de código como una serie de ajustes de configuración le permite construir sistemas complejos utilizando código de terceros con una codificación mínima de su parte.
El ejemplo proporcionado en la pregunta simplemente toca la superficie del poder expresivo que puede proporcionar una biblioteca de objetos DI correctamente factorizada. Con algo 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 de prueba del 100% de una pequeña aplicación 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 proporcione este nivel de capacidad de prueba.
Tiene razón en que una aplicación de solo decenas 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 ocurre con los patrones de diseño más poderosos, las ganancias se encuentran a medida que continúa agregando nuevas funciones al sistema.
En resumen, las aplicaciones basadas en DI a gran escala son más fáciles de depurar y de comprender. Si bien la configuración de Xml no está 'comprobada en tiempo de compilación', todos los servicios de aplicación que este autor conoce proporcionarán al desarrollador mensajes de error si intentan inyectar un objeto que tiene una interfaz incompatible en otro objeto. Y la mayoría proporciona una función de "verificación" que cubre todas las configuraciones de objetos conocidas. Esto se hace fácil y rápidamente comprobando que el objeto A a inyectar implemente la interfaz requerida por el objeto B para todas las inyecciones de objetos configuradas.
fuente
Esta es una pregunta un poco cargada, pero tiendo a estar de acuerdo en que grandes cantidades de configuración xml realmente no representan un gran beneficio. Me gusta que mis aplicaciones sean lo más ligeras posible en cuanto a dependencias, incluidos los marcos pesados.
Simplifican el código muchas veces, pero también tienen una sobrecarga en la complejidad que hace que rastrear problemas sea bastante difícil (he visto estos problemas de primera mano, y me sentiría mucho más cómodo tratando con Java puro).
Supongo que depende un poco del estilo y con lo que se sienta cómodo ... ¿le gusta volar su propia solución y tener la ventaja de conocerla al revés, o confiar en soluciones existentes que pueden resultar difíciles cuando la configuración no lo es? ¿Es justo? Todo es una compensación.
Sin embargo, la configuración XML es algo que me molesta un poco ... trato de evitarlo a toda costa.
fuente
Cada vez que puede cambiar su código a datos, está dando un paso en la dirección correcta.
Codificar cualquier cosa como datos significa que su código en sí es más general y reutilizable. También significa que sus datos pueden especificarse en un idioma que se ajuste exactamente a ellos.
Además, un archivo XML puede leerse en una GUI o alguna otra herramienta y manipularse fácilmente de forma pragmática. ¿Cómo harías eso con el ejemplo de código?
Constantemente estoy 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 parece inconcebible que la gente cree un menú en código en lugar de como datos; debería ser obvio que hacerlo en código es simplemente incorrecto debido a la repetición.
fuente
La razón para usar un contenedor DI es que no es necesario tener mil millones de propiedades preconfiguradas en su código que son simplemente getters y setters. ¿Realmente desea codificar todos aquellos con la nueva X ()? Claro, puede tener un valor predeterminado, pero el contenedor DI permite la creación de singletons, lo cual es extremadamente fácil y le permite concentrarse 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 verificar nulos 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 a usar XFire (no CeltiXFire, solo usamos Java 1.4). La aplicación usó 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 de beans de Spring. Entonces, si estuviéramos usando Spring para configurar los servicios, los beans podrían haberse usado. 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 cambio real que necesitaba hacer requería que sobrecargara una sola clase.
Pero, gracias al archivo services.xml, tuve que crear cuatro clases nuevas, 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:
En cambio, se parecía más a esto:
Entonces, el resultado neto es que se tuvieron que agregar cuatro clases Java adicionales, en su mayoría inútiles, al código base para lograr el efecto que lograron una clase adicional y alguna información de contenedor de dependencia simple. Esta no es la "excepción que prueba la regla", esta ES la regla ... manejar las peculiaridades en el código es mucho más limpio cuando las propiedades ya están provistas en un contenedor DI y simplemente las está cambiando para adaptarse a una situación especial, lo que ocurre con mayor frecuencia.
fuente
Tengo tu respuesta
Obviamente, existen compensaciones en cada enfoque, pero los archivos de configuración XML externalizados son útiles para el desarrollo empresarial en el que se utilizan sistemas de compilación para compilar el código y no su IDE. Al usar 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 (que podría ser doloroso tener que actualizar manualmente cada vez que compila). El dolor es mayor cuando su sistema de compilación extrae código de algún sistema de control de versiones. La modificación de valores simples en tiempo de compilación requeriría que cambie un archivo, lo confirme, compile y luego revertir cada vez para cada cambio. Estos no son cambios que desee realizar en su control de versiones.
Otros casos de uso útiles relacionados con el sistema de compilación y las configuraciones externas:
Actualización: Todos los ejemplos anteriores se referían a cosas que no necesariamente requerían dependencias de clases. Pero puede crear fácilmente casos en los que se necesitan tanto un objeto complejo como la automatización, por ejemplo:
fuente
No es necesario que vuelva 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 con otro con solo 1 cambio en el archivo de configuración.
fuente
Puede insertar una nueva implementación para novia. Por lo tanto, se puede inyectar nueva hembra sin volver a compilar su código.
(Lo anterior asume que Female y HotFemale implementan la misma interfaz GirlfFriend)
fuente
En el mundo .NET, la mayoría de los frameworks de IoC proporcionan configuración de código y XML.
StructureMap y Ninject, por ejemplo, utilizan interfaces fluidas para configurar contenedores. Ya no está obligado a utilizar archivos de configuración XML. Spring, que también existe en .NET, se basa en gran medida en archivos XML ya que es su interfaz de configuración principal histórica, pero aún es posible configurar contenedores mediante programación.
fuente
Facilidad de combinar configuraciones parciales en una configuración final completa.
Por ejemplo, en aplicaciones web, el modelo, la vista y los controladores generalmente se especifican en archivos de configuración separados. Utilice el enfoque declarativo, puede cargar, por ejemplo:
O cargue con una interfaz de usuario diferente y algunos controladores adicionales:
Hacer lo mismo en código requiere una infraestructura para combinar configuraciones parciales. No es imposible de hacer en código, pero ciertamente es más fácil de hacer usando un marco de IoC.
fuente
A menudo, lo importante es quién cambia la configuración después de que se escribió el programa. Con la configuración en el código, implícitamente asume que la persona que lo cambia tiene las mismas habilidades y acceso al código fuente, etc., que tenía el autor original.
En los sistemas de producción, es muy práctico extraer algún subconjunto de configuraciones (por ejemplo, la edad en su ejemplo) a un archivo XML y permitir, por ejemplo, al administrador del sistema o al personal de soporte cambiar el valor sin darles el poder total sobre el código fuente u otras configuraciones, o simplemente para aislarlos de las complejidades.
fuente
Desde una perspectiva de Spring, 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á usando desde una biblioteca. Spring 3 incluye un método para definir la configuración DI usando Java similar a la configuración DI enrollada 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 un marco DI. Tiene muchas otras características, incluida la gestión de transacciones y AOP. La configuración de Spring XML combina todos estos conceptos. A menudo, en el mismo archivo de configuración, estoy especificando las dependencias de los beans, la configuración de transacciones y agregando beans con ámbito de sesión que realmente se manejan con AOP en segundo plano. Encuentro que la configuración XML proporciona un mejor lugar para administrar todas estas funciones. También creo que la configuración basada en anotaciones y la configuración XML se amplían mejor que la configuración basada en Java.
Pero veo su punto y no hay nada de malo en definir la configuración de inyección de dependencia en Java. Normalmente lo hago yo mismo en pruebas unitarias y cuando estoy trabajando en un proyecto lo suficientemente pequeño como para no haber agregado un marco DI. Normalmente no especifico la configuración en Java porque para mí ese es el tipo de código de plomería que estoy tratando de evitar escribir cuando elegí usar Spring. Sin embargo, esa es una preferencia, no significa que la configuración XML sea superior a la configuración basada en Java.
fuente
Spring también tiene un cargador de propiedades. Usamos este método para establecer variables que dependen del entorno (por ejemplo, desarrollo, pruebas, aceptación, producción, ...). Esta podría ser, por ejemplo, la cola para escuchar.
Si no hay ninguna razón por la cual la propiedad cambiaría, tampoco hay razón para configurarla de esta manera.
fuente
Su caso es muy simple y, por lo tanto, no necesita un contenedor IoC (Inversión de control) como Spring. Por otro lado, cuando "programa en interfaces, no en implementaciones" (lo cual es una buena práctica en OOP), puede tener un código como este:
(tenga en cuenta que el tipo de myService es IService, una interfaz, no una implementación concreta). Ahora puede ser útil dejar que su contenedor de IoC proporcione automáticamente la instancia concreta correcta de IService durante la inicialización; cuando tiene muchas interfaces y muchas implementaciones, puede resultar engorroso hacerlo a mano. Los principales beneficios de un contenedor de IoC (marco de inyección de dependencia) son:
fuente
Inicializar en un archivo de configuración XML simplificará su trabajo de depuración / adaptación con un cliente que tiene su aplicación implementada en sus computadoras. (Porque no requiere recompilación + reemplazo de archivos binarios)
fuente
Una de las razones más atractivas es el " principio de Hollywood ": no nos llame, le llamaremos. No es necesario que un componente realice búsquedas en otros componentes y servicios por sí mismo; en su lugar, se le proporcionan automáticamente. En Java, esto significa que ya no es necesario realizar búsquedas JNDI dentro del componente.
También es mucho más fácil probar un componente de forma aislada: en lugar de darle una implementación real de los componentes que necesita, simplemente usa simulacros (posiblemente generados automáticamente).
fuente