sin dependency injectionEdit
en el siguiente ejemplo de Java, la clase Client contiene una variable miembro de servicio inicializada por el constructor 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.
// An example without dependency injectionpublic class Client { // Internal reference to the service used by this client private ExampleService service; // Constructor Client() { // Specify a specific implementation in the constructor instead of using dependency injection service = new ExampleService(); } // Method within this client that uses the services public String greet() { return "Hello " + service.getName(); }}
La inyección de dependencias 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 a continuación.
tipos de inyección de dependenciaeditar
hay al menos tres formas en que un objeto cliente puede recibir una referencia a un módulo externo:
inyección de constructor las dependencias se proporcionan a través del constructor de clase de un cliente. inyección de setter el cliente expone un método de setter que el inyector utiliza para inyectar la dependencia. inyección de interfaz la interfaz de la dependencia proporciona un método de inyector que inyectará la dependencia en cualquier cliente que se le pase., Los clientes deben implementar una interfaz que exponga un método setter que acepte la dependencia.
otros tiposeditar
es posible que los frameworks DI tengan otros tipos de inyección más allá de los presentados anteriormente.
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 dependencias, lo que hace que el código heredado sea comprobable. En particular, en el lenguaje Java, es posible usar la reflexión para hacer públicos los atributos privados al probar y así Aceptar inyecciones por asignación.,
algunos intentos de inversión del Control no proporcionan la eliminación completa 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.
constructor injectionEdit
Este método requiere que el cliente proporcione un parámetro en un constructor para la dependencia.
Setter injectionEdit
Este método requiere que el cliente proporcione un método setter para la dependencia.,
Interface injectionEdit
esto es simplemente el cliente que publica una interfaz de rol a los métodos setter de las dependencias del cliente. Se puede utilizar para establecer cómo el inyector debe hablar con el cliente al inyectar dependencias.
// Service setter interface.public interface ServiceSetter { public void setService(Service service);}// Client classpublic class Client implements ServiceSetter { // Internal reference to the service used by this client. private Service service; // Set the service that this client is to use. @Override public void setService(Service service) { this.service = service; }}
Constructor injection comparisonEdit
se prefiere cuando todas las dependencias se pueden construir primero porque se puede usar para garantizar que el objeto cliente esté siempre en un estado válido, en lugar de tener algunas de sus referencias de dependencia null (no estar configuradas)., Sin embargo, por sí solo, carece de la flexibilidad para que sus dependencias cambien Más tarde. Esto puede ser un primer paso para hacer que el cliente sea inmutable y, por lo tanto, seguro.
setter injection comparisonEdit
requiere que el cliente proporcione un método setter para cada dependencia. Esto 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 a inyectar, es difícil para el cliente asegurarse de que todas las dependencias se inyectan antes de que el cliente pueda ser proporcionado para su uso.,
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 puede ser dejada nula simplemente por el inyector fallando en llamar a su setter. Esto obliga a verificar que la inyección se completó desde el momento en que se ensambla el cliente hasta el momento en que se usa.
Interface injection comparisonEdit
la ventaja de la inyección de interfaz es que las dependencias pueden ser completamente ignorantes de sus clientes, pero aún pueden recibir una referencia a un nuevo cliente y, al usarla, enviar una referencia a sí mismo al cliente., De esta manera, las dependencias se convierten en inyectores. La clave es que el método de inyección (que podría ser un método clásico de setter) se proporciona a través de una interfaz.
todavía se necesita un ensamblador para introducir el cliente y sus dependencias. El ensamblador tomaría una referencia al cliente, la enviaría a la interfaz setter que establece esa dependencia, y la pasaría a ese objeto de dependencia que daría la vuelta y pasaría una referencia a sí mismo de vuelta al cliente.,
para que la inyección de interfaz tenga valor, la dependencia debe hacer algo además de simplemente pasar una referencia a sí misma. Esto podría estar actuando como una fábrica o sub-ensamblador para resolver otras dependencias, abstrayendo así algunos detalles del ensamblador principal. Podría ser un recuento de referencia para que la dependencia sepa cuántos clientes lo están usando. Si la dependencia mantiene una colección de clientes, más tarde podría inyectarlos a todos con una instancia diferente de sí mismo.,
ejemplos de Ensambladoeditar
ensamblar manualmente en main a mano es una forma de implementar la inyección de dependencias.
el ejemplo anterior construye el gráfico de objetos manualmente y luego lo invoca en un punto para comenzar a funcionar. Importante tener en cuenta es que este inyector no es puro. Utiliza uno de los objetos que construye. Tiene una relación puramente de construcción con ExampleService, pero mezcla la construcción y el uso del cliente. 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 objetos inyectado por dependencia necesita al menos un punto de entrada (preferiblemente solo uno) para comenzar todo.
la construcción Manual en el método principal puede no ser tan sencilla y puede implicar llamar a Constructores, fábricas u otros patrones de construcción también. Esto puede ser bastante avanzado y abstracto., La línea se cruza de la inyección manual de dependencias a la inyección de dependencias del marco una vez que el código de construcción ya no es personalizado para la aplicación y en su lugar es universal.
Frameworks 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.
Frameworks como Spring permiten externalizar detalles de ensamblado en 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. Un gráfico de objetos largo y complejo se puede definir 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().
en el ejemplo anterior, el cliente y el servicio no han tenido que sufrir ningún cambio para ser proporcionados por Spring. Se les permite seguir siendo simples POJOs. Esto muestra cómo Spring puede conectar servicios y clientes que son completamente ignorantes de su existencia. Esto no podría decirse si se agregaran anotaciones de primavera a las clases., Al evitar que las anotaciones y llamadas específicas de Spring se extiendan entre muchas clases, el sistema solo depende vagamente de Spring. Esto puede ser importante si el sistema tiene la intención de sobrevivir a la primavera.
la elección de mantener POJOs puros no viene sin costo. 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. Resolver dependencias puede ser simple si siguen una convención como la coincidencia por tipo o por nombre. Esto es elegir Convención sobre configuración., También es discutible que, al refactorizar a otro marco, eliminar anotaciones específicas del marco sería una parte trivial de la tarea y muchas anotaciones de inyección ahora están estandarizadas.
@Componentpublic class ExampleService { public String getName() { return "World!"; }}
Assembly comparisonEdit
las diferentes implementaciones de inyectores (fábricas, localizadores de servicios y contenedores de inyección de dependencias) no son tan diferentes en lo que respecta a la inyección de dependencias. Lo que hace toda la diferencia es donde se les permite ser utilizados., Mover llamadas a una fábrica o un localizador de servicio fuera del cliente y en main y de repente main hace un contenedor de inyección de dependencia bastante bueno.
moviendo todo el conocimiento del inyector, un cliente limpio, libre de conocimiento del mundo exterior, se queda atrás. Sin embargo, cualquier objeto que utilice otros objetos puede ser considerado cliente. El objeto que contiene main no es una excepción. Este objeto principal no utiliza inyección de dependencias. En realidad está utilizando el patrón de localizador de servicios. Esto no se puede evitar porque la elección de las implementaciones de servicios debe hacerse en algún lugar.,
externalizar las dependencias en archivos de configuración no cambia este hecho. Lo que hace que esta realidad sea parte de un buen diseño es que el localizador de servicios no se extiende por toda la base de código. Se limita a un lugar por aplicación. Esto deja al resto de la base de código libre para usar la inyección de dependencias para hacer clientes limpios.
Dependency Injection PatternEdit
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 dependencias es más útil cuando se construye un gráfico de objetos 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 de objetos. 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 las aplicaciones.,
AngularJS exampleEdit
en el framework 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, típicamente usando el operador
new
. - El componente puede buscar la dependencia, haciendo referencia a una variable global.
- El componente puede tener la dependencia pasada a él donde se necesita.
Las dos primeras opciones de crear o buscar dependencias no son óptimas porque codifican la dependencia al componente., Esto hace que sea difícil, si no imposible, modificar las dependencias. Esto es especialmente problemático en pruebas, donde a menudo es deseable proporcionar dependencias simuladas para el aislamiento de pruebas.
la tercera opción es la más viable, ya que elimina la responsabilidad de localizar la dependencia del componente. La dependencia se entrega simplemente al componente.
en el ejemplo anterior, SomeClass
no se ocupa de crear o localizar la dependencia greeter, simplemente se le entrega el greeter cuando se crea una instancia.,
esto es deseable, pero pone la responsabilidad de hacerse con la dependencia del código que construye SomeClass
.
para gestionar la responsabilidad de 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.
Aquí hay un ejemplo de uso del servicio de inyector:
cree un nuevo inyector que pueda proporcionar componentes definidos en el módulo myModule
y solicite nuestro servicio de bienvenida desde el inyector., (Esto generalmente se hace automáticamente por el bootstrap de AngularJS).
var injector = angular.injector();var greeter = injector.get('greeter');
pedir dependencias resuelve el problema de la codificación dura, pero también significa que el inyector debe pasarse a través de la aplicación. Pasar el inyector rompe la Ley de Deméter., Para remediar esto, utilizamos una notación declarativa en nuestras plantillas HTML, para entregar la responsabilidad de crear componentes al inyector, como en este ejemplo:
<div ng-controller="MyController"> <button ng-click="sayHello()">Hello</button></div>
function MyController($scope, greeter) { $scope.sayHello = function() { greeter.greet('Hello World'); };}
Cuando AngularJS compila el HTML, procesa el ng-controller
directiva, que a su vez 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
se remite al inyector para instanciar la clase, puede satisfacer todas las dependencias de MyController
sin que el controlador sepa nunca 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 rompe la Ley de Deméter.
C # Edit
Ejemplo de inyección de Constructor, inyección de Setter e inyección de interfaz en C#