En ingeniería de software , la inyección de dependencia es una técnica en la que un objeto recibe otros objetos de los que depende. Estos otros objetos se denominan dependencias. En la relación típica de "uso", el objeto receptor se denomina cliente y el objeto pasado (es decir, "inyectado") se denomina servicio . El código que pasa el servicio al cliente puede ser de muchos tipos y se llama inyector. En lugar de que el cliente especifique qué servicio utilizará, el inyector le dice al cliente qué servicio utilizar. La "inyección" se refiere al paso de una dependencia (un servicio) al objeto (un cliente) que lo usaría.
El servicio pasa a formar parte del estado del cliente . [1] Pasar el servicio al cliente, en lugar de permitir que el cliente cree o encuentre el servicio , es el requisito fundamental del patrón.
La intención detrás de la inyección de dependencia es lograr la separación de preocupaciones de construcción y uso de objetos. Esto puede aumentar la legibilidad y la reutilización del código.
La inyección de dependencia es una forma de la técnica más amplia de inversión de control . Un cliente que quiera llamar a algunos servicios no debería tener que saber cómo construir esos servicios. En cambio, el cliente delega la responsabilidad de proporcionar sus servicios a un código externo (el inyector). El cliente no puede llamar al código del inyector; [2] es el inyector el que construye los servicios. A continuación, el inyector inyecta (pasa) los servicios al cliente que puede que ya exista o que también pueda haber sido construido por el inyector. Luego, el cliente usa los servicios. Esto significa que el cliente no necesita saber sobre el inyector, cómo construir los servicios o incluso qué servicios reales está utilizando. El cliente solo necesita conocer las interfaces intrínsecas de los servicios porque estas definen cómo el cliente puede utilizar los servicios. Esto separa la responsabilidad de "uso" de la responsabilidad de "construcción".
Usos
La inyección de dependencia resuelve problemas como: [3]
- ¿Cómo puede una aplicación o clase ser independiente de cómo se crean sus objetos?
- ¿Cómo se puede especificar la forma en que se crean los objetos en archivos de configuración separados?
- ¿Cómo puede una aplicación admitir diferentes configuraciones?
La creación de objetos directamente dentro de la clase a menudo puede ser inflexible porque compromete a la clase con objetos e implementaciones particulares. Esto puede dificultar el cambio de la instanciación en tiempo de ejecución, especialmente en lenguajes compilados donde cambiar los objetos subyacentes a veces puede requerir volver a compilar el código fuente.
Con Dependency Injection, la clase ya no es responsable de crear instancias de los objetos que requiere. Esas responsabilidades se transfieren a servicios independientes.
Descripción general
Cuando vas y sacas cosas del refrigerador por ti mismo, puedes causar problemas. Puede dejar la puerta abierta, puede conseguir algo que mamá o papá no quieren que usted tenga. Incluso podría estar buscando algo que ni siquiera tenemos o que ha caducado.
Lo que debería hacer es indicar una necesidad, "Necesito algo para beber con el almuerzo", y luego nos aseguraremos de que tenga algo cuando se siente a comer.La inyección de dependencias separa la creación de las dependencias de un cliente del comportamiento del cliente, lo que permite que los diseños de programas se acoplen libremente [7] y sigan los principios de inversión de dependencia y responsabilidad única . [4] [8] Contrasta directamente con el patrón del localizador de servicios , que permite a los clientes conocer el sistema que utilizan para encontrar dependencias.
Una inyección, la unidad básica de inyección de dependencia, no es un mecanismo nuevo o personalizado. Funciona de la misma forma que funciona el " paso de parámetros ". [9] Hacer referencia al "paso de parámetros" como una inyección conlleva la implicación adicional de que se está haciendo para aislar al cliente de los detalles.
Una inyección también se trata de lo que está en control del paso (nunca el cliente) y es independiente de cómo se logra el paso, ya sea pasando una referencia o un valor.
La inyección de dependencia involucra cuatro roles:
- los objetos de servicio que se utilizarán
- el objeto cliente que depende de los servicios que utiliza
- las interfaces que definen cómo el cliente puede utilizar los servicios
- el inyector , que se encarga de construir los servicios e inyectarlos en el cliente
Como analogía,
- servicio: un automóvil eléctrico, de gasolina, híbrido o diésel
- cliente: un conductor que usa el automóvil de la misma manera independientemente del motor
- Interfaz: automática , garantiza que el conductor no tenga que comprender los detalles de los cambios de marcha.
- inyector: el padre que compró el automóvil para el conductor y decidió de qué tipo
Cualquier objeto que pueda utilizarse puede considerarse un servicio . Cualquier objeto que utilice otros objetos puede considerarse un cliente . Los nombres no tienen nada que ver con la finalidad de los objetos y todo que ver con el papel que juegan los objetos en cualquier inyección.
Las interfaces son los tipos que el cliente espera que sean sus dependencias. Un problema es lo que hacen accesible. Pueden ser realmente tipos de interfaz implementados por los servicios, pero también pueden ser clases abstractas o incluso los propios servicios concretos , aunque esto último violaría DIP [10] y sacrificaría el desacoplamiento dinámico que permite las pruebas. Solo se requiere que el cliente no sepa cuáles son y por lo tanto nunca los trate como concretos, digamos construyéndolos o ampliándolos.
El cliente no debe tener un conocimiento concreto de la implementación específica de sus dependencias. Solo debe conocer el nombre de la interfaz y la API . Como resultado, el cliente no necesitará cambiar incluso si cambia lo que está detrás de la interfaz. Sin embargo, si la interfaz se refactoriza de una clase a un tipo de interfaz (o viceversa), será necesario volver a compilar el cliente. [11] Esto es significativo si el cliente y los servicios se publican por separado. Este desafortunado acoplamiento es uno que la inyección de dependencia no puede resolver.
El inyector introduce los servicios en el cliente. A menudo, también construye al cliente. Un inyector puede conectar un gráfico de objeto muy complejo tratando un objeto como un cliente y luego como un servicio para otro cliente. El inyector puede ser en realidad muchos objetos trabajando juntos, pero puede que no sea el cliente. El inyector puede tener otros nombres como: ensamblador, proveedor, contenedor, fábrica, constructor, resorte, código de construcción o principal.
La inyección de dependencia se puede aplicar como una disciplina, una que pide que todos los objetos separen la construcción y el comportamiento. Depender de un marco DI para realizar la construcción puede llevar a prohibir el uso de la nueva palabra clave o, de manera menos estricta, solo permitir la construcción directa de objetos de valor . [12] [13] [14] [15]
Taxonomía
La inversión de control (IoC) es más general que la inyección de dependencia. En pocas palabras, IoC significa dejar que otro código lo llame en lugar de insistir en hacer la llamada. Un ejemplo de IoC sin inyección de dependencia es el patrón del método de plantilla . Aquí, el polimorfismo se logra mediante subclases , es decir, herencia . [dieciséis]
La inyección de dependencia implementa IoC a través de la composición, por lo que a menudo es idéntica a la del patrón de estrategia , pero mientras que el patrón de estrategia está diseñado para que las dependencias sean intercambiables durante la vida útil de un objeto , en la inyección de dependencia puede ser que solo se use una instancia única de una dependencia . [17] Esto todavía logra polimorfismo, pero a través de delegación y composición .
Marcos de inyección de dependencia
Los marcos de aplicación como CDI y su implementación Weld , Spring , Guice , Play framework , Salta , Glassfish HK2 , Dagger y Managed Extensibility Framework (MEF) admiten la inyección de dependencia, pero no es necesario que lo haga. [18] [19]
Ventajas
- La inyección de dependencia permite al cliente la flexibilidad de ser configurable. Solo se fija el comportamiento del cliente. El cliente puede actuar sobre cualquier cosa que admita la interfaz intrínseca que espera el cliente. [20]
- La inyección de dependencia se puede utilizar para externalizar los detalles de configuración de un sistema en archivos de configuración, lo que permite que el sistema se reconfigure sin volver a compilarlo. Se pueden escribir configuraciones separadas para diferentes situaciones que requieren diferentes implementaciones de componentes. Esto incluye, pero no se limita a, pruebas. [21]
- Debido a que la inyección de dependencias no requiere ningún cambio en el comportamiento del código, se puede aplicar al código heredado como refactorización . El resultado son clientes que son más independientes y que son más fáciles de probar unitariamente de forma aislada utilizando stubs u objetos simulados que simulan otros objetos que no están bajo prueba. Esta facilidad de prueba es a menudo el primer beneficio que se nota cuando se usa la inyección de dependencia. [22]
- La inyección de dependencia permite a un cliente eliminar todo el conocimiento de una implementación concreta que necesita usar. Esto ayuda a aislar al cliente del impacto de los cambios y defectos de diseño. Promueve la reutilización, la capacidad de prueba y la capacidad de mantenimiento. [23]
- Reducción del código repetitivo en los objetos de la aplicación, ya que todo el trabajo para inicializar o configurar dependencias es manejado por un componente de proveedor. [23]
- La inyección de dependencia permite el desarrollo simultáneo o independiente. Dos desarrolladores pueden desarrollar clases de forma independiente que se utilicen entre sí, mientras que solo necesitan conocer la interfaz a través de la cual se comunicarán las clases. Los complementos a menudo son desarrollados por tiendas de terceros que ni siquiera hablan con los desarrolladores que crearon el producto que utiliza los complementos. [24]
- La inyección de dependencia reduce el acoplamiento entre una clase y su dependencia. [25] [26]
Desventajas
- La inyección de dependencia crea clientes que exigen que los detalles de configuración sean proporcionados por el código de construcción. Esto puede resultar oneroso cuando se dispone de valores predeterminados obvios. [24]
- La inyección de dependencia puede hacer que el código sea difícil de rastrear (leer) porque separa el comportamiento de la construcción. Esto significa que los desarrolladores deben consultar más archivos para seguir el rendimiento de un sistema. [24]
- Los marcos de inyección de dependencia se implementan con reflexión o programación dinámica. Esto puede dificultar el uso de la automatización IDE, como "buscar referencias", "mostrar jerarquía de llamadas" y refactorizaciones seguras. [27]
- La inyección de dependencia generalmente requiere un mayor esfuerzo de desarrollo inicial, ya que uno no puede convocar a algo correcto cuando y donde se necesita, sino que debe solicitar que se inyecte y luego asegurarse de que se haya inyectado. [28]
- La inyección de dependencias obliga a la complejidad a salir de las clases y entrar en los vínculos entre clases que no siempre pueden ser deseables o fáciles de gestionar. [29]
- La inyección de dependencia puede fomentar la dependencia de un marco de inyección de dependencia. [29] [30] [31]
Estructura
Diagrama de secuencia y clase UML
En el diagrama de clases de UML anterior , la Client
clase que requiere los objetos ServiceA
y ServiceB
no crea una instancia de las clases ServiceA1
y ServiceB1
directamente. En cambio, una Injector
clase crea los objetos y los inyecta en Client
, lo que hace que sea Client
independiente de cómo se crean los objetos (qué clases concretas se instancian).
El diagrama de secuencia UML muestra las interacciones en tiempo de ejecución: El Injector
objeto crea los objetos ServiceA1
y ServiceB1
. A partir de entonces, Injector
crea el Client
objeto e inyecta los objetos ServiceA1
y ServiceB1
.
Ejemplos de
Sin inyección de dependencia
En el siguiente ejemplo de Java , la clase Client contiene una variable de miembro de servicio que es inicializada por el constructor de Client . El cliente controla qué implementación del servicio se utiliza y controla su construcción. En esta situación, se dice que el cliente tiene una dependencia codificada en ExampleService.
// Un ejemplo sin inyección de dependencia public class Client { // Referencia interna al servicio utilizado por este cliente. Private ExampleService service ; // Constructor Client () { // Especifica una implementación específica en el constructor en lugar de usar el servicio de inyección de dependencia = new ExampleService (); } // Método dentro de este cliente que usa los servicios públicos String greet () { return "Hola" + servicio . getName (); } }
La inyección de dependencia es una técnica alternativa para inicializar la variable miembro en lugar de crear explícitamente un objeto de servicio como se muestra arriba. Podemos ajustar este ejemplo utilizando las diversas técnicas descritas e ilustradas en las subsecciones siguientes.
Tipos de inyección de dependencia
Hay al menos tres formas en que un objeto cliente puede recibir una referencia a un módulo externo: [33]
- inyección de constructor
- Las dependencias se proporcionan a través del constructor de clases de un cliente.
- inyección de setter
- El cliente expone un método setter que usa el inyector para inyectar la dependencia.
- inyección de interfaz
- La interfaz de la dependencia proporciona un método inyector que inyectará la dependencia en cualquier cliente que se le pase. Los clientes deben implementar una interfaz que exponga un método de establecimiento que acepte la dependencia.
Otros tipos
Es posible que los marcos de DI tengan otros tipos de inyección además de los presentados anteriormente. [34]
Los marcos de prueba también pueden usar otros tipos. Algunos marcos de prueba modernos ni siquiera requieren que los clientes acepten activamente la inyección de dependencia, lo que hace que el código heredado sea comprobable. En particular, en el lenguaje Java, es posible utilizar la reflexión para hacer públicos los atributos privados al realizar pruebas y, por lo tanto, aceptar inyecciones por asignación. [35]
Algunos intentos de inversión de control no proporcionan la eliminación total de la dependencia, sino que simplemente sustituyen una forma de dependencia por otra. Como regla general, si un programador no puede mirar nada más que el código del cliente y decir qué marco se está utilizando, entonces el cliente tiene una dependencia codificada en el marco.
Inyección de constructor
Este método requiere que el cliente proporcione un parámetro en un constructor para la dependencia.
// Cliente constructor ( servicio de servicio ) { // Guarde la referencia al servicio pasado dentro de este cliente this . servicio = servicio ; }
Inyección de setter
Este método requiere que el cliente proporcione un método de establecimiento para la dependencia.
// Método de establecimiento public void setService ( Servicio de servicio ) { // Guarde la referencia al servicio pasado dentro de este cliente. esto . servicio = servicio ; }
Inyección de interfaz
Esto es simplemente el cliente que publica una interfaz de rol para los métodos de establecimiento de las dependencias del cliente. Se puede utilizar para establecer cómo el inyector debe hablar con el cliente al inyectar dependencias.
// Interfaz de establecimiento de servicios. ServiceSetter de interfaz pública { public void setService ( servicio de servicio ); } // clase de cliente público de clase de cliente implementos ServiceSetter { referencia // interna para el servicio utilizado por este cliente. servicio de servicio privado ; // Establecer el servicio que utilizará este cliente. @Override public void setService ( Servicio de servicio ) { this . servicio = servicio ; } }
Comparación de inyección de constructor
Se prefiere cuando todas las dependencias se pueden construir primero porque se puede utilizar para garantizar que el objeto cliente siempre esté en un estado válido, en lugar de que algunas de sus referencias de dependencia sean nulas (no se establezcan). Sin embargo, por sí solo, carece de la flexibilidad para cambiar sus dependencias más adelante. Este puede ser un primer paso para hacer que el cliente sea inmutable y, por lo tanto, seguro para subprocesos .
// Constructor de cliente ( Servicio de servicio , Servicio otherService ) { si ( servicio == nula ) { lanzar nuevos InvalidParameterException ( "servicio no debe ser nulo" ); } if ( otherService == null ) { throw new InvalidParameterException ( "otherService no debe ser nulo" ); } // Guarde las referencias de servicio dentro de este cliente this . servicio = servicio ; esto . otherService = otherService ; }
Comparación de inyección de setter
Requiere que el cliente proporcione un método de establecimiento para cada dependencia. Esto le da la libertad de manipular el estado de las referencias de dependencia en cualquier momento. Esto ofrece flexibilidad, pero si hay más de una dependencia para inyectar, es difícil para el cliente asegurarse de que todas las dependencias se inyecten antes de que el cliente pueda proporcionarse para su uso.
// Establecer el servicio que utilizará este cliente public void setService ( Servicio servicio ) { if ( servicio == null ) { lanzar new InvalidParameterException ( "el servicio no debe ser nulo" ); } esto . servicio = servicio ; }// Establecer el otro servicio que será utilizado por este cliente public void setOtherService ( Servicio otherService ) { if ( otherService == null ) { throw new InvalidParameterException ( "otherService no debe ser nulo" ); } esto . otherService = otherService ; }
Debido a que estas inyecciones ocurren de forma independiente, no hay forma de saber cuándo el inyector ha terminado de cablear al cliente. Una dependencia se puede dejar nula simplemente porque el inyector no llama a su establecedor. Esto obliga a verificar que la inyección se completó desde que se ensambla el cliente hasta que se usa.
// Establecer el servicio que utilizará este cliente public void setService ( Servicio de servicio ) { this . servicio = servicio ; }// Establecer el otro servicio que utilizará este cliente public void setOtherService ( Service otherService ) { this . otherService = otherService ; }// Verifique las referencias de servicio de este cliente private void validateState () { if ( service == null ) { throw new IllegalStateException ( "el servicio no debe ser nulo" ); } if ( otherService == null ) { lanzar nueva IllegalStateException ( "otherService no debe ser nulo" ); } }// Método que utiliza las referencias de servicio public void doSomething () { validateState (); servicio . doYourThing (); otherService . doYourThing (); }
Comparación de inyección de interfaz
La ventaja de la inyección de interfaz es que las dependencias pueden ignorar por completo a sus clientes, pero aún pueden recibir una referencia a un nuevo cliente y, usándola, enviar una referencia a sí mismo al cliente. De esta forma, las dependencias se convierten en inyectores. La clave es que el método de inyección (que podría ser simplemente un método setter clásico) se proporciona a través de una interfaz.
Todavía se necesita un ensamblador para presentar el cliente y sus dependencias. El ensamblador tomaría una referencia al cliente, la enviaría a la interfaz de establecimiento que establece esa dependencia y la pasaría a ese objeto de dependencia que se daría la vuelta y pasaría una referencia a sí mismo al cliente.
Para que la inyección de interfaz tenga valor, la dependencia debe hacer algo además de simplemente devolverse una referencia a sí misma. Esto podría actuar como una fábrica o un sub-ensamblador para resolver otras dependencias, abstrayendo así algunos detalles del ensamblador principal. Podría ser un recuento de referencias para que la dependencia sepa cuántos clientes lo están usando. Si la dependencia mantiene una colección de clientes, luego podría inyectarlos a todos con una instancia diferente de sí misma.
// Interfaz de establecimiento de servicios. ServiceSetter de interfaz pública { public void setService ( servicio de servicio ); } // clase de cliente público de clase de cliente implementos ServiceSetter { referencia // interna para el servicio utilizado por este cliente. servicio de servicio privado ; // Establecer el servicio que utilizará este cliente. @Override public void setService ( Servicio de servicio ) { this . servicio = servicio ; } }// Injector class public class ServiceInjector { Set < ServiceSetter > clientes ; public void inject ( cliente ServiceSetter ) { clientes . agregar ( cliente ); cliente . setService ( nuevo ServiceFoo ()); } public void switchToBar () { para ( Cliente cliente : clientes ) { cliente . setService ( new ServiceBar ()); } } } // Clases de servicio public class ServiceFoo implementa Service {} public class ServiceBar implementa Service {}
Ejemplos de ensamblaje
El ensamblaje manual de main a mano es una forma de implementar la inyección de dependencia.
Inyector de clase pública { public static void main ( String [] args ) { // Primero construye las dependencias Service service = new ExampleService (); // Inyectar el servicio, estilo constructor Cliente cliente = nuevo Cliente ( servicio ); // Usa los objetos System . fuera . println ( cliente . saludar ()); } }
El ejemplo anterior construye el gráfico de objetos manualmente y luego lo invoca en un punto para que comience a funcionar. Es importante tener en cuenta que este inyector no es puro. Utiliza uno de los objetos que construye. Tiene una relación puramente de construcción con ExampleService, pero combina la construcción y el uso de Client. Esto no debería ser común. Sin embargo, es inevitable. Al igual que el software orientado a objetos necesita un método estático no orientado a objetos como main () para comenzar, un gráfico de objeto inyectado de dependencia necesita al menos un punto de entrada (preferiblemente solo uno) para que todo comience.
La construcción manual en el método principal puede no ser tan sencilla y también puede implicar llamar a constructores , fábricas u otros patrones de construcción . Esto puede ser bastante avanzado y abstracto. La línea se cruza de la inyección de dependencia manual a la inyección de dependencia del marco una vez que el código de construcción ya no está personalizado para la aplicación y, en cambio, es universal. [36]
Los marcos como Spring pueden construir estos mismos objetos y conectarlos antes de devolver una referencia al cliente. Toda mención del ExampleService concreto se puede mover del código a los datos de configuración.
importar org.springframework.beans.factory.BeanFactory ; import org.springframework.context.ApplicationContext ; import org.springframework.context.support.ClassPathXmlApplicationContext ; Inyector de clase pública { public static void main ( String [] args ) { // - Ensamblaje de objetos - // BeanFactory beanfactory = new ClassPathXmlApplicationContext ( "Beans.xml" ); Cliente cliente = ( Cliente ) beanfactory . getBean ( "cliente" ); // - Usando objetos - // System . fuera . println ( cliente . saludar ()); } }
Los marcos como Spring permiten que los detalles del ensamblaje se externalicen en los archivos de configuración. Este código (arriba) construye objetos y los conecta de acuerdo con Beans.xml (abajo). ExampleService todavía se construye a pesar de que solo se menciona a continuación. Se puede definir un gráfico de objeto largo y complejo de esta manera y la única clase mencionada en el código sería la que tiene el método de punto de entrada, que en este caso es greet ().
xml version = "1.0" encoding = "UTF-8"?> xmlns = "http://www.springframework.org/schema/beans" xmlns: xsi = "http://www.w3.org / 2001 / XMLSchema-instance " xsi: schemaLocation = " http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd " > id = "service" class = "ExampleService" > id = "client" class = "Client" > value = "service" />
En el ejemplo anterior, el Cliente y el Servicio no han tenido que someterse a ningún cambio para que Spring los proporcione. Se les permite seguir siendo simples POJO . [37] [38] [39] Esto muestra cómo Spring puede conectar servicios y clientes que ignoran por completo su existencia. Esto no se podría decir si se añadieran anotaciones Spring a las clases. Al evitar que las anotaciones y llamadas específicas de Spring se extiendan entre muchas clases, el sistema depende solo ligeramente de Spring. [30] Esto puede ser importante si el sistema pretende sobrevivir a Spring.
La decisión de mantener los POJO puros no es gratuita. En lugar de gastar el esfuerzo en desarrollar y mantener archivos de configuración complejos, es posible simplemente usar anotaciones para marcar clases y dejar que Spring haga el resto del trabajo. La resolución de dependencias puede ser sencilla si siguen una convención, como hacer coincidir por tipo o por nombre. Se trata de elegir la convención sobre la configuración . [40] También es discutible que, al refactorizar a otro marco, eliminar las anotaciones específicas del marco sería una parte trivial de la tarea [41] y muchas anotaciones de inyección ahora están estandarizadas. [42] [43]
importar org.springframework.beans.factory.BeanFactory ; import org.springframework.context.ApplicationContext ; import org.springframework.context.annotation.AnnotationConfigApplicationContext ;public class Injector { public static void main ( String [] args ) { // Ensamblar los objetos BeanFactory beanfactory = new AnnotationConfigApplicationContext ( MyConfiguration . class ); Cliente cliente = beanfactory . getBean ( Client . clase );// Usa los objetos System . fuera . println ( cliente . saludar ()); } }
importar org.springframework.context.annotation.Bean ; import org.springframework.context.annotation.ComponentScan ; importar org.springframework.context.annotation.Configuration ;@ComponentScan public class MyConfiguration { @Bean public Client client ( servicio ExampleService ) { return new Client ( servicio ); } }
@Componente clase pública ExampleService { public String getName () { return "World!" ; } }
Comparación de ensamblajes
Las diferentes implementaciones de inyectores (fábricas, localizadores de servicios y contenedores de inyección de dependencia) no son tan diferentes en lo que respecta a la inyección de dependencia. Lo que marca la diferencia es dónde se permite su uso. Mover las llamadas a una fábrica o un localizador de servicios fuera del cliente y en main y, de repente, main hace un contenedor de inyección de dependencia bastante bueno.
Al sacar todo el conocimiento del inyector, se deja atrás a un cliente limpio, libre de conocimiento del mundo exterior. Sin embargo, cualquier objeto que utilice otros objetos puede considerarse un cliente. El objeto que contiene main no es una excepción. Este objeto principal no utiliza la inyección de dependencia. En realidad, está utilizando el patrón de localizador de servicios. Esto no se puede evitar porque la elección de las implementaciones del servicio debe realizarse en algún lugar.
Externalizar las dependencias en archivos de configuración no cambia este hecho. Lo que hace que esta realidad forme parte de un buen diseño es que el localizador de servicios no se extiende por toda la base del código. Se limita a un lugar por aplicación. Esto deja el resto de la base de código libre para usar la inyección de dependencia para hacer clientes limpios.
Patrón de inyección de dependencia
Los ejemplos hasta ahora han sido ejemplos demasiado simples sobre la construcción de una cadena. Sin embargo, el patrón de inyección de dependencia es más útil cuando se construye un gráfico de objeto donde los objetos se comunican a través de mensajes. Los objetos construidos en main durarán toda la vida del programa. El patrón típico es construir el gráfico y luego llamar a un método en un objeto para enviar el flujo de control al gráfico del objeto. Así como main es el punto de entrada al código estático, este método es el punto de entrada al código no estático de la aplicación.
public static void main ( String [] args ) lanza IOException { // Código de construcción. Saludador saludador = nuevo Saludador ( Sistema . Fuera ); // Pueden ser muchas líneas que conectan muchos objetos // Código de comportamiento. saludador . saludar (); // Esta es una llamada a un método en un objeto en el gráfico de objetos }class Greeter { public void greet () { this . fuera . println ( "¡Hola mundo!" ); } Pública Greeter ( PrintStream cabo ) { esto . out = out ; } Salida PrintStream privada ; }
Ejemplo de AngularJS
En el marco AngularJS , solo hay tres formas en que un componente (objeto o función) puede acceder directamente a sus dependencias:
- El componente puede crear la dependencia, normalmente utilizando el
new
operador. - El componente puede buscar la dependencia, haciendo referencia a una variable global.
- Se puede pasar la dependencia al componente donde sea necesario.
Las dos primeras opciones de crear o buscar dependencias no son óptimas porque codifican de forma rígida la dependencia del componente. Esto hace que sea difícil, si no imposible, modificar las dependencias. Esto es especialmente problemático en las pruebas, donde a menudo es deseable proporcionar dependencias simuladas para el aislamiento de la prueba.
La tercera opción es la más viable, ya que elimina la responsabilidad de localizar la dependencia del componente. La dependencia simplemente se transfiere al componente.
function SomeClass ( saludador ) { this . saludador = saludador ; }SomeClass . prototipo . hacerAlgo = función ( nombre ) { esto . saludador . saludar ( nombre ); }
En el ejemplo anterior, SomeClass
no se trata de crear o ubicar la dependencia del saludador, simplemente se le entrega al saludador cuando se crea una instancia.
Esto es deseable, pero pone la responsabilidad de apoderarse de la dependencia en el código que construye SomeClass
.
Para gestionar la responsabilidad de la creación de dependencias, cada aplicación AngularJS tiene un inyector. El inyector es un localizador de servicios que se encarga de la construcción y búsqueda de dependencias.
A continuación, se muestra un ejemplo del uso del servicio de inyectores:
// Proporcione la información de cableado en un módulo var myModule = angular . módulo ( 'myModule' , []);// Enséñele al inyector cómo crear un servicio de bienvenida. // greeter depende del servicio $ window. // El servicio de saludo es un objeto que // contiene un método de saludo. myModule . factory ( 'saludo' , función ( $ ventana ) { retorno { saludo : función ( texto ) { $ ventana . alerta ( texto ); } }; });
Cree un nuevo inyector que pueda proporcionar componentes definidos en el myModule
módulo y solicite nuestro servicio de bienvenida al inyector. (Esto generalmente se hace automáticamente mediante el bootstrap de AngularJS).
var inyector = angular . inyector ([ 'myModule' , 'ng' ]); var greeter = inyector . get ( 'saludador' );
Solicitar dependencias resuelve el problema de la codificación rígida, pero también significa que el inyector debe pasarse por toda la aplicación. Pasar el inyector infringe la Ley de Deméter . Para remediar esto, usamos una notación declarativa en nuestras plantillas HTML, para pasar la responsabilidad de crear componentes al inyector, como en este ejemplo:
< div ng-controller = "MyController" > < button ng-click = "sayHello ()" > Hola button > div >
function MyController ( $ alcance , saludo ) { $ alcance . sayHello = function () { saludador . saludar ( 'Hola mundo' ); }; }
Cuando AngularJS compila el HTML, procesa la ng-controller
directiva, que a su vez le pide al inyector que cree una instancia del controlador y sus dependencias.
injector.instantiate (MyController);
Todo esto se hace entre bastidores. Debido a que ng-controller
difiere al inyector para crear una instancia de la clase, puede satisfacer todas las dependencias de MyController
sin que el controlador sepa sobre el inyector. El código de la aplicación simplemente declara las dependencias que necesita, sin tener que lidiar con el inyector. Esta configuración no viola la Ley de Deméter .
C#
Ejemplo de inyección de constructor , inyección de Setter e inyección de interfaz en C #
usando el sistema ;espacio de nombres DependencyInjection { // interfaz Una para la biblioteca de interfaz IGamepadFunctionality { Cadena GetGamepadName (); void SetVibrationPower ( flotar InPower ); } // Implementación concreta de la clase de funcionalidad del controlador xbox XBoxGamepad : IGamepadFunctionality { readonly String GamepadName = "XBox Controller" ; flotar VibrationPower = 1.0f ; public String GetGamepadName () => GamepadName ; public void SetVibrationPower ( float InPower ) => VibrationPower = Math . Abrazadera ( InPower , 0.0f , 1.0f ); } // Implementación concreta de la clase de funcionalidad del controlador de playstation PlaystationJoystick : IGamepadFunctionality { readonly String ControllerName = "Playstation controller" ; flotar VibratingPower = 100.0f ; public String GetGamepadName () => ControllerName ; pública vacío SetVibrationPower ( float InPower ) => VibratingPower = Math . Abrazadera ( InPower * 100.0f , 0.0f , 100.0f ); } // Implementación concreta de la clase de funcionalidad del controlador de vapor SteamController : IGamepadFunctionality { readonly String JoystickName = "Steam controller" ; doble vibración = 1.0 ; public String GetGamepadName () => JoystickName ; pública vacío SetVibrationPower ( float InPower ) => vibrante = Convert . ToDouble ( Math . Clamp ( InPower , 0.0f , 1.0f )); } // Una interfaz para inyecciones de funcionalidad de gamepad interface IGamepadFunctionalityInjector { void InjectFunctionality ( IGamepadFunctionality InGamepadFunctionality ); } clase CGamepad : IGamepadFunctionalityInjector { IGamepadFunctionality _GamepadFunctionality ; public CGamepad () { } // Constructor de inyección public CGamepad ( IGamepadFunctionality InGamepadFunctionality ) => _GamepadFunctionality = InGamepadFunctionality ; // Inyección de Setter public void SetGamepadFunctionality ( IGamepadFunctionality InGamepadFunctionality ) => _GamepadFunctionality = InGamepadFunctionality ; // Inyección de interfaz public void InjectFunctionality ( IGamepadFunctionality InGamepadFunctionality ) => _GamepadFunctionality = InGamepadFunctionality ; public void Showcase () { String Message = String . Format ( "Estamos usando {0} en este momento, ¿quieres cambiar la potencia de vibración? \ R \ n" , _GamepadFunctionality . GetGamepadName ()); Consola . WriteLine ( mensaje ); } } enumeración EPlatforms : byte { Xbox , Playstation , Steam } class CGameEngine { EPlatforms _Platform ; CGamepad _Gamepad ; public void SetPlatform ( EPlatforms InPlatform ) { _Platform = InPlatform ; switch ( _Platform ) { caso EPlatforms . Xbox : // inyecta dependencia en la clase XBoxGamepad a través de Constructor Injection _Gamepad = new CGamepad ( new XBoxGamepad ()); romper ; caso EPlatforms . Playstation : _Gamepad = new CGamepad (); // inyecta dependencia en la clase PlaystationJoystick a través de Setter Injection _Gamepad . SetGamepadFunctionality ( nuevo PlaystationJoystick ()); romper ; caso EPlatforms . Steam : _Gamepad = new CGamepad (); // inyecta dependencia en la clase SteamController a través de Interface Injection _Gamepad . InjectFunctionality ( nuevo SteamController ()); romper ; } _Gamepad . Escaparate (); } } class Program { static void Main ( string [] args ) { Console . WriteLine ( "¡Hola mundo!" ); CGameEngine Engine = nuevo CGameEngine (); Motor . SetPlatform ( EPlatforms . Steam ); Motor . SetPlatform ( EPlatforms . Xbox ); Motor . SetPlatform ( EPlatforms . Playstation ); } } }
Ver también
- Lenguaje de descripción de arquitectura
- Patrón de fábrica
- Inversión de control
- Plug-in (informática)
- Patrón de estrategia
- AngularJS
- Patrón de localizador de servicios
- Parámetro (programación informática)
- Quaject
Referencias
- ^ IT, titanio. "James Shore: inyección de dependencia desmitificada" . www.jamesshore.com . Consultado el 18 de julio de 2015 .
- ^ "Principio de Hollywood" . c2.com . Consultado el 19 de julio de 2015 .
- ^ "El patrón de diseño de inyección de dependencia: problema, solución y aplicabilidad" . w3sDesign.com . Consultado el 12 de agosto de 2017 .
- ^ a b Seeman, Mark (octubre de 2011). Inyección de dependencias en .NET . Publicaciones Manning. pag. 4. ISBN 9781935182504.
- ^ "Inyección de dependencia en NET" (PDF) . philkildea.co.uk . pag. 4 . Consultado el 18 de julio de 2015 .
- ^ "¿Cómo explicar la inyección de dependencia a un niño de 5 años?" . stackoverflow.com . Consultado el 18 de julio de 2015 .
- ^ Seemann, Mark. "La inyección de dependencia es un acoplamiento flojo" . blog.ploeh.dk . Consultado el 28 de julio de 2015 .
- ^ Niko Schwarz, Mircea Lungu, Oscar Nierstrasz, "Seuss: Desacoplamiento de responsabilidades de métodos estáticos para una configurabilidad detallada", Journal of Object Technology, Volumen 11, no. 1 (abril de 2012), págs. 3: 1-23
- ^ "Pasar información a un método o un constructor (Tutoriales de Java ™> Aprendizaje del lenguaje Java> Clases y objetos)" . docs.oracle.com . Consultado el 18 de julio de 2015 .
- ^ "Un curry de principio de inversión de dependencia (DIP), inversión de control (IoC), inyección de dependencia (DI) y contenedor de IoC - CodeProject" . www.codeproject.com . Consultado el 8 de agosto de 2015 .
- ^ "Cómo forzar" el programa a una interfaz "sin usar una interfaz java en java 1.6" . programmers.stackexchange.com . Consultado el 19 de julio de 2015 .
- ^ "Para" nuevo "o no para" nuevo "…" . Consultado el 18 de julio de 2015 .
- ^ "Cómo escribir código comprobable" . www.loosecouplings.com . Consultado el 18 de julio de 2015 .
- ^ "Escribir código limpio y comprobable" . www.ethanresnick.com . Consultado el 18 de julio de 2015 .
- ^ Sironi, Giorgio. "Cuándo inyectar: la distinción entre nuevos e inyectables - Invisible a la vista" . www.giorgiosironi.com . Consultado el 18 de julio de 2015 .
- ^ "Inversión de Control vs Inyección de Dependencia" . stackoverflow.com . Consultado el 5 de agosto de 2015 .
- ^ "¿Cuál es la diferencia entre el patrón de estrategia y la inyección de dependencia?" . stackoverflow.com . Consultado el 18 de julio de 2015 .
- ^ "Inyección de dependencia! = Usando un contenedor DI" . www.loosecouplings.com . Consultado el 18 de julio de 2015 .
- ^ "Oveja Negra» DIY-DI »Imprimir" . blacksheep.parry.org . Archivado desde el original el 27 de junio de 2015 . Consultado el 18 de julio de 2015 .
- ^ https://python.astrotech.io/design-patterns/structural/dependency-injection.html
- ^ http://python-dependency-injector.ets-labs.org/introduction/di_in_python.html
- ^ https://visualstudiomagazine.com/articles/2014/07/01/larger-applications.aspx
- ^ a b "El programa Java Community Process (SM) - JSR: solicitudes de especificación de Java - detalle JSR # 330" . jcp.org . Consultado el 18 de julio de 2015 .
- ^ a b c https://dzone.com/articles/how-dependency-injection-di-works-in-spring-java-a
- ^ "el canuk urbano, eh: sobre la inyección de dependencia y la violación de preocupaciones de encapsulación" . www.bryancook.net . Consultado el 18 de julio de 2015 .
- ^ "El patrón de diseño de inyección de dependencia" . msdn.microsoft.com . Consultado el 18 de julio de 2015 .
- ^ https://www.freecodecamp.org/news/a-quick-intro-to-dependency-injection-what-it-is-and-when-to-use-it-7578c84fa88f/
- ^ https://www.professionalqa.com/dependency-injection
- ^ a b "¿Cuáles son las desventajas de usar Dependency Injection?" . stackoverflow.com . Consultado el 18 de julio de 2015 .
- ^ a b "Inversión de inyección de dependencia - Codificador limpio" . sites.google.com . Consultado el 18 de julio de 2015 .
- ^ "Desacoplamiento de su aplicación de su marco de inyección de dependencia" . InfoQ . Consultado el 18 de julio de 2015 .
- ^ "El patrón de diseño de inyección de dependencia - Estructura y colaboración" . w3sDesign.com . Consultado el 12 de agosto de 2017 .
- ^ Martin Fowler (23 de enero de 2004). "Inversión de contenedores de control y el patrón de inyección de dependencia - Formas de inyección de dependencia" . Martinfowler.com . Consultado el 22 de marzo de 2014 .
- ^ "Yan - Tipos de inyección de dependencia" . Yan.codehaus.org. Archivado desde el original el 18 de agosto de 2013 . Consultado el 11 de diciembre de 2013 .
- ^ "AccessibleObject (Java Platform SE 7)" . docs.oracle.com . Consultado el 18 de julio de 2015 .
- ^ Riehle, Dirk (2000), Diseño de marco: un enfoque de modelado de roles (PDF) , Instituto Federal Suizo de Tecnología
- ^ "Consejos de primavera: un POJO con anotaciones no es sencillo" . Archivado desde el original el 15 de julio de 2015 . Consultado el 18 de julio de 2015 .
- ^ "Anotaciones en POJO - ¿una bendición o una maldición? | Techtracer" . 2007-04-07 . Consultado el 18 de julio de 2015 .
- ^ Módulos dinámicos Pro Spring para plataformas de servicio OSGi . APulse. 2009-02-17. ISBN 9781430216124. Consultado el 6 de julio de 2015 .
- ^ "Blog del Capitán Debug: ¿La 'Convención sobre la configuración' va demasiado lejos?" . www.captaindebug.com . Consultado el 18 de julio de 2015 .
- ^ Decker, Colin. "¿Cuál es el problema con @Inject? | Devlog de Colin" . blog.cgdecker.com . Consultado el 18 de julio de 2015 .
- ^ Morling, Gunnar (18 de noviembre de 2012). "Dagger - Un nuevo marco de inyección de dependencia de Java" . Dagger - Un nuevo marco de inyección de dependencia de Java - Reflexiones de un adicto a la programación . Consultado el 18 de julio de 2015 .
- ^ "El programa Java Community Process (SM) - JSR: solicitudes de especificación de Java - detalle JSR # 330" . www.jcp.org . Consultado el 18 de julio de 2015 .
enlaces externos
- Raíz de composición por Mark Seemann
- Una guía para principiantes sobre la inyección de dependencia
- Inyección de dependencias y objetos comprobables: diseño de objetos comprobables y poco acoplados - Jeremy Weiskotten; Dr. Dobb's Journal , mayo de 2006.
- Patrones de diseño: inyección de dependencia - MSDN Magazine, septiembre de 2005
- El artículo original de Martin Fowler que introdujo el término inyección de dependencia
- P de EAA: complemento
- La rica herencia de ingeniería detrás de la inyección de dependencia - Andrew McVeigh - Una historia detallada de la inyección de dependencia.
- ¿Qué es la inyección de dependencia? - Una explicación alternativa - Jakob Jenkov
- Escribir código más comprobable con inyección de dependencia - Developer.com, octubre de 2006
- Descripción general del marco de extensibilidad administrado - MSDN
- Descripción anticuada del mecanismo de dependencia por Hunt 1998
- Refactorice su camino hacia un contenedor de inyección de dependencia
- Entendiendo DI en PHP
- No necesita un contenedor de inyección de dependencia