2010-10-19 16 views
13

Me considero un programador experimentado y entiendo el concepto básico de inyección de dependencia. Por otro lado, la mayor parte de mi experiencia se basa en escribir un número relativamente bajo, un código numérico de un solo hombre. No tengo ninguna experiencia trabajando en grandes proyectos empresariales.Marcos de inyección de dependencia: ¿Cómo funcionan?

Dado este trasfondo, no puedo por la vida de entender por qué alguien necesitaría un marco para hacer la inyección de dependencia. ¿Puede alguien darme una breve descripción de cómo funciona un marco así, sin entrar en muchos detalles, y explicar cómo hace la vida más fácil que solo hacer los suyos?

Editar: He recibido algunas buenas respuestas aquí. ¿Estoy en lo cierto al decir que un marco de DI básicamente te brinda una manera conveniente de crear fábricas accesibles globalmente que crean/devuelven instancias de dependencias cada vez que un objeto las solicita? Si es así, he estado haciendo cosas como esta de forma muy ad hoc en mi código todo el tiempo, pero nunca pensé usar ningún tipo de marco formal/pesado para ello.

Respuesta

7

DI se trata de desacoplar las dependencias entre las clases.

Con DI, sus clases ya no tienen referencias para implementar clases.El patrón de fábrica se acerca al DI, pero es diferente porque una clase de fábrica es una dependencia no deseada (que, por ejemplo, perjudicará las pruebas unitarias).

DI tampoco es necesariamente un asunto global o de aplicación; es posible configurar diferentes esquemas de dependencia para la misma aplicación y las mismas clases. Incluso puede haber dependencias de tiempo de ejecución. DI puede incluso determinar el ciclo de vida o el alcance en el que los objetos deben vivir (alcance de solicitud, alcance de sesión, etc.).

Tampoco pesa demasiado o es intrusivo, si observa implementaciones DI livianas como Google’s Guice. No es por nada lo llaman el nuevo "nuevo".

+0

Estoy aceptando esto porque responde mi pregunta. Sin embargo, busqué y leí un poco sobre Guice y todavía soy escéptico sobre si configurarlo es realmente más fácil o menos frágil que simplemente escribir unas pocas funciones/clases de fábrica y/o tener algunas banderas de configuración almacenadas de la manera adecuada para lo que estás tratando de lograr. – dsimcha

+0

Lo veo como una mejora de esos patrones, ya que también resuelve la dependencia de las clases de fábrica. También es menos codificante, con una simple anotación creas fábricas enteras. Por lo tanto, menos errores. – Kdeveloper

+0

Cuanto más lo pienso, más creo que un marco DI puede ser conveniente. Es algo que usaría si estuviera en la lib estándar, y haría la vida ligeramente más fácil, pero no algo para lo que me metiera con dependencias de terceros. Mi idioma principal es D, soy desarrollador de su biblioteca estándar y las funciones de metaprogramación de D'son bastante buenas, así que estoy pensando a medias en escribir un marco DI muy simple y liviano (como en un módulo) para incluirlo en el D estándar lib. – dsimcha

5

Wikipedia tiene una buena descripción de los beneficios y las implementaciones.

http://en.wikipedia.org/wiki/Inversion_of_control

me parece contenedores IoC como Spring para ser útil, porque de todos los demás "cosas" que hacen, al igual que la gestión de transacciones, y el código que proporcionan, como plantillas de código, clases base de pruebas, etc., los cuales son eminentemente útiles. Además, en el mundo de Java, creo que IoC es una reacción a un estándar Java EE que muchos desarrolladores encontraron demasiado torpe. IoC, como ha sido implementado por Spring, le permite diseñar componentes de servicio POJO, en su mayor parte, lo que supongo es más fácil que los aros que Java EE hizo saltar a la gente.

Mucho de esto, o al menos algo de eso, es la masturbación mental, hasta cierto punto, lo que significa que las ventajas de la IoC proporcionadas por los contenedores no se utilizan por completo en la mayoría de los proyectos que las usan.
"Deberíamos usar DI porque podremos intercambiar fácilmente implementaciones de nuestros DAO en el futuro, yippeeeeee" - rara vez sucede en mi experiencia. La gente los usa porque se han convertido en un estándar, y una palabra de moda en mi humilde opinión. No estoy diciendo que eso sea algo malo; solo que la parte IoC del software estándar no es realmente la razón por la que se usan.

3

Me gusta mucho @hvgotcodes responder, pero pensé en agregar algunos puntos, ya que soy un gran fan de DI/IoC.

Utilizamos DI, por las siguientes ventajas:

  1. Comprobabilidad. Escribimos pruebas unitarias contra un Repositorio (por ejemplo), a través de interfaces. Ahora, para las pruebas de integración, usamos DI para inyectar un repositorio "real" para nuestras pruebas. Pero, ¿qué hay de las pruebas unitarias? No queremos probar el Repositorio/base de datos "real". Entonces usamos DI para "inyectar" un repositorio simulado. Mucho de esto tiene que ver con la magia de las interfaces (por supuesto, podrías "codificar" tus pruebas para usar el repositorio falso cuando sea necesario).

  2. Punto de inyección central. - Registro AKA. Cada proyecto/módulo tiene un registro donde activamos/desactivamos los componentes. Por ejemplo, en nuestro proyecto de prueba, cuando "algo" solicita un IRepository, inyectamos un MockRepository. En nuestro proyecto de capa de servicio, cuando "algo" solicita un IRepository, inyectamos un EntityFrameworkRepository.

  3. Excelente Scope Management. Esto depende del contenedor DI (utilizamos StructureMap para .NET).Pero puede configurar sus objetos para que tengan vidas útiles Singleton, HTTP-Context, ThreadLocal en una sola línea cuando configure su Registro. De nuevo, esto es bueno porque manejas los objetos en un lugar central. Sus componentes no tienen idea de su tiempo de vida, pueden enfocarse en su trabajo y nada más.

  4. One-line obturador de los componentes. Esto es posible gracias al registro (punto n. ° 2). Pero en este momento estamos usando Entity Framework para la persistencia. Lo único que "sabe" sobre esto es el Registro. Entonces, si algún día sale algo mejor, todo lo que tenemos que hacer es crear otra implementación de IRepository y pasar una línea en el Registro. Esto es muy posible en aplicaciones de larga duración. Usamos L2SQL para v1 de nuestro sitio web, ahora estamos usando EF para v2. Si usamos DI con v1, el tiempo de corte casi se reduciría a la mitad. Pero todo está vinculado a la capa de persistencia, por lo que básicamente estamos reescribiendo el sitio web desde cero.

Los beneficios de DI/COI están muy vistos cuando se combina una gran cantidad de diferentes principios tales como repositorio, POCO de, DDD, Interfaces, N-Tier.

No lo usaría para una aplicación pequeña que es poco probable que evolucione.

Esa es mi granito de arena.

+0

Los elementos 1 y 4 son una función de inyección de dependencia, no de contenedores, que es lo que el OP está preguntando. –

+0

@Mauricio Scheffer: un tema relacionado, ¿no crees? Veo la pregunta sobre la Inyección de Dependencia, no solo "por qué son buenos los contenedores DI". – RPM1984

2

Si comienza la ruta DI, muy pronto tendrá constrictores que toman 20 parámetros para las diversas dependencias que necesita el objeto. Obtener esos 20 parámetros requerirá obtener otros 20 parámetros para construirlos. Y, luego, al final (¿al principio?) De todo, se dará cuenta de que se ha acoplado a una implementación concreta de su interfaz cuidadosamente considerada (llamando al constructor). Y luego, 6 meses después, agregará una nueva dependencia, que requiere que haga un seguimiento de todas las llamadas existentes y las cambie también.

Un marco DI básicamente se encarga de esa fontanería para usted. Al interponerse entre usted y el constructor, puede interrogar a config (tal vez XML, tal vez código) que le dice qué hacer cuando necesita un objeto concreto.

Los conceptos básicos son bastante sencillos con cualquier lenguaje que tenga algún tipo de introspección (registre un tipo concreto para satisfacer una interfaz, cuando necesite una instancia de una interfaz, ejemplifique el tipo. Recorra el gráfico creado por el constructor y repita .) donde se complica es cuando también desea controlar el tiempo de vida de los objetos (esta clase Logger solo debe crearse una instancia y luego reutilizarse. Esta DataAccessFacade debe crearse una instancia por hilo, etc.) o elegir dinámicamente cómo satisfacer una dependencia. Un marco DI normalmente proporcionará tanto instalaciones de creación de objetos (averiguar las dependencias requeridas para el constructor) como una instalación de Localizador de Servicios para que no todo tenga que pasarse en el constructor o como un parámetro para los métodos. Eso le permite registrar tipos con constructores arbitrariamente complejos, obtener una instancia de la misma y no tener que preocuparse por cuándo o cómo compartir una instancia, cuándo se construye, cuándo se destruye o si se acopla al tipo real.

La creación de bibliotecas con DI también permite a los usuarios intercambiar dependencias cuando sea necesario, lo que proporciona una gran flexibilidad cuando se combina con interfaces o herencia sin complicar el código con argumentos de constructor o método para cada dependencia.

+0

¿alguna vez has visto una implementación intercambiada? – hvgotcodes

+0

@hvgotcodes - Claro. Los más obvios son para probar, o tal vez para iniciar sesión. Fachadas de hardware, acceso a datos, componentes de 2 niveles a 3 niveles, etc. No quiero decir que pueda aislarse o diseñar para todas las eventualidades futuras, pero ciertamente puede aliviar el dolor. –

Cuestiones relacionadas