design-patterns - ejemplo - inyeccion de dependencias spring
¿Qué es la inyección de dependencia? (30)
¿Qué es la inyección de dependencia?
La inyección de dependencia (DI) significa desacoplar los objetos que dependen uno del otro. Diga que el objeto A depende del objeto B, por lo que la idea es desacoplar estos objetos entre sí. No necesitamos codificar el objeto utilizando una nueva palabra clave en lugar de compartir dependencias con objetos en tiempo de ejecución a pesar del tiempo de compilación. Si hablamos de
Cómo funciona la inyección de dependencia en primavera:
No necesitamos codificar el objeto con una nueva palabra clave, en lugar de definir la dependencia del bean en el archivo de configuración. El contenedor de muelle se encargará de enganchar todo.
Inversión de Control (IOC)
La COI es un concepto general y puede expresarse de muchas maneras diferentes y la inyección de dependencia es un ejemplo concreto de la COI.
Dos tipos de inyección de dependencia:
- Inyección Constructor
- Inyección de Setter
1. Inyección de dependencia basada en constructor:
El DI basado en constructor se logra cuando el contenedor invoca a un constructor de clase con varios argumentos, cada uno de los cuales representa una dependencia de otra clase.
public class Triangle {
private String type;
public String getType(){
return type;
}
public Triangle(String type){ //constructor injection
this.type=type;
}
}
<bean id=triangle" class ="com.test.dependencyInjection.Triangle">
<constructor-arg value="20"/>
</bean>
2. Inyección de dependencia basada en Setter:
El DI basado en Setter se logra mediante el contenedor que llama a los métodos de establecimiento en sus beans después de invocar un constructor sin argumentos o un método de fábrica estático sin argumentos para instanciar su bean.
public class Triangle{
private String type;
public String getType(){
return type;
}
public void setType(String type){ //setter injection
this.type = type;
}
}
<!-- setter injection -->
<bean id="triangle" class="com.test.dependencyInjection.Triangle">
<property name="type" value="equivialteral"/>
NOTA: Es una buena regla general usar argumentos de constructor para dependencias obligatorias y configuradores para dependencias opcionales. Tenga en cuenta que si utilizamos una anotación basada en @ La anotación requerida en un configurador se puede utilizar para hacer definidores como dependencias requeridas.
Ya se han publicado varias preguntas con preguntas específicas sobre la inyección de dependencia , por ejemplo, cuándo usarlo y qué marcos de trabajo hay para ello. Sin embargo,
¿Qué es la inyección de dependencia y cuándo / por qué debe o no debe usarse?
¿La "inyección de dependencia" no significa simplemente usar constructores parametrizados y definidores públicos?
El artículo de James Shore muestra los siguientes ejemplos para comparar .
Constructor sin inyección de dependencia:
public class Example {
private DatabaseThingie myDatabase;
public Example() {
myDatabase = new DatabaseThingie();
}
public void doStuff() {
...
myDatabase.getData();
...
}
}
Constructor con inyección de dependencia:
public class Example {
private DatabaseThingie myDatabase;
public Example(DatabaseThingie useThisDatabaseInstead) {
myDatabase = useThisDatabaseInstead;
}
public void doStuff() {
...
myDatabase.getData();
...
}
}
Básicamente, en lugar de hacer que tus objetos creen una dependencia o pidan a un objeto de fábrica que haga uno para ellos, pasas las dependencias necesarias al objeto externamente y lo conviertes en un problema ajeno. Este "alguien" es un objeto más arriba en el gráfico de dependencia o un inyector de dependencia (marco) que construye el gráfico de dependencia. Una dependencia como la estoy usando aquí es cualquier otro objeto que el objeto actual necesita para contener una referencia.
Una de las principales ventajas de la inyección de dependencia es que puede hacer que las pruebas sean mucho más fáciles. Supongamos que tienes un objeto que en su constructor hace algo como:
public SomeClass() {
myObject = Factory.getObject();
}
Esto puede ser problemático cuando todo lo que quiere hacer es ejecutar algunas pruebas unitarias en SomeClass, especialmente si myObject es algo que hace un acceso complejo a un disco o a la red. Así que ahora estás mirando a myObject burlándose pero también interceptando la llamada de fábrica. Difícil. En su lugar, pase el objeto como un argumento al constructor. Ahora ha movido el problema a otra parte, pero las pruebas pueden ser mucho más fáciles. Simplemente haga un objeto ficticio myObject y envíelo. El constructor ahora se vería un poco como:
public SomeClass (MyClass myObject) {
this.myObject = myObject;
}
Este es un estilo de inyección de dependencia, a través del constructor. Varios mecanismos son posibles.
- Como se señaló en los comentarios, una alternativa común es definir un constructor de no hacer nada y tener las dependencias inyectadas a través de los establecedores de propiedades (h / t @MikeVella).
- Martin Fowler documenta una tercera alternativa (h / t @MarcDix), donde las clases implementan explícitamente una interfaz para las dependencias que desean que se inyecten.
Cuando no se usa la inyección de dependencia (como en las clases que hacen demasiado trabajo en sus constructores, etc.), tiende a ser mucho más difícil aislar los componentes en las pruebas unitarias.
En 2013, cuando escribí esta respuesta, este fue un tema importante en Google Testing Blog . Esta sigue siendo la mayor ventaja para mí, ya que es posible que no siempre necesite la flexibilidad adicional en su diseño en tiempo de ejecución (por ejemplo, para el localizador de servicios o patrones similares), pero a menudo necesita poder aislar sus clases durante las pruebas.
Imaginemos que quieres ir a pescar:
Sin la inyección de dependencia, necesita cuidar de todo usted mismo. Necesitas encontrar un bote, comprar una caña de pescar, buscar un cebo, etc. Es posible, por supuesto, pero te pone mucha responsabilidad. En términos de software, significa que debe realizar una búsqueda de todas estas cosas.
Con la inyección de dependencia, otra persona se encarga de toda la preparación y pone a su disposición el equipo requerido. Recibirá ("se inyectará") el bote, la caña de pescar y el cebo, todo listo para usar.
La inyección de dependencia es una práctica en la que los objetos se diseñan de manera que reciben instancias de los objetos de otras piezas de código, en lugar de construirlos internamente. Esto significa que cualquier objeto que implemente la interfaz que requiere el objeto puede sustituirse sin cambiar el código, lo que simplifica las pruebas y mejora el desacoplamiento.
Por ejemplo, considera estas clases:
public class PersonService {
public void addManager( Person employee, Person newManager ) { ... }
public void removeManager( Person employee, Person oldManager ) { ... }
public Group getGroupByManager( Person manager ) { ... }
}
public class GroupMembershipService() {
public void addPersonToGroup( Person person, Group group ) { ... }
public void removePersonFromGroup( Person person, Group group ) { ... }
}
En este ejemplo, la implementación de PersonService::addManager
y PersonService::addManager
PersonService::removeManager
necesitaría una instancia del GroupMembershipService para realizar su trabajo. Sin la inyección de dependencia, la forma tradicional de hacer esto sería crear una instancia de un nuevo servicio de GroupMembershipService
en el constructor de PersonService
y usar ese atributo de instancia en ambas funciones. Sin embargo, si el constructor de GroupMembershipService
tiene varias cosas que requiere o, lo que es peor, hay algunos "configuradores" de inicialización que deben llamarse en GroupMembershipService
, el código crece bastante rápido, y PersonService
ahora no solo depende de GroupMembershipService
sino También todo lo demás que GroupMembershipService
depende. Además, el enlace a GroupMembershipService
está codificado en el PersonService
que significa que no puede " GroupMembershipService
" un GroupMembershipService
con fines de prueba, o usar un patrón de estrategia en diferentes partes de su aplicación.
Con la inyección de dependencia, en lugar de crear una instancia de GroupMembershipService
dentro de su PersonService
, puede pasarlo al constructor PersonService
, o bien agregar una propiedad (getter y setter) para establecer una instancia local de este. Esto significa que su PersonService
ya no tiene que preocuparse por cómo crear un GroupMembershipService
, solo acepta los que se le brindan y trabaja con ellos. Esto también significa que cualquier cosa que sea una subclase de GroupMembershipService
, o implemente la interfaz de GroupMembershipService
, puede ser "inyectada" en el PersonService
, y el PersonService
no necesita saber sobre el cambio.
La mejor definición que he encontrado hasta ahora es una de James Shore :
"Inyección de dependencia" es un término de 25 dólares para un concepto de 5 centavos. [...] La inyección de dependencia significa dar a un objeto sus variables de instancia. [...].
Hay un artículo de Martin Fowler que también puede ser útil.
La inyección de dependencia consiste básicamente en proporcionar los objetos que un objeto necesita (sus dependencias) en lugar de que él mismo los construya. Es una técnica muy útil para realizar pruebas, ya que permite burlar o rechazar las dependencias.
Las dependencias se pueden inyectar en objetos por muchos medios (como inyección de constructor o inyección de colocador). Incluso se pueden usar marcos de inyección de dependencia especializados (por ejemplo, Spring) para hacer eso, pero ciertamente no son necesarios. No necesitas esos marcos para tener inyección de dependencia. La creación de instancias y el paso de objetos (dependencias) explícitamente es una inyección tan buena como la inyección por marco.
La respuesta aceptada es buena, pero me gustaría agregar a esto que DI es muy similar a la clásica de evitar constantes codificadas en el código.
Cuando usas una constante como el nombre de una base de datos, la mueves rápidamente desde el interior del código a un archivo de configuración y pasas una variable que contiene ese valor al lugar donde se necesita. La razón para hacerlo es que estas constantes generalmente cambian con más frecuencia que el resto del código. Por ejemplo, si desea probar el código en una base de datos de prueba.
DI es similar a esto en el mundo de la programación orientada a objetos. Los valores en lugar de los literales constantes son objetos completos, pero la razón para mover el código que los crea a partir del código de clase es similar: los objetos cambian con más frecuencia que el código que los usa. Un caso importante donde se necesita tal cambio son las pruebas.
Probemos un ejemplo simple con las clases de Automóvil y Motor , cualquier automóvil necesita un motor para ir a cualquier parte, al menos por ahora. Así que debajo de cómo se verá el código sin inyección de dependencia.
public class Car
{
public Car()
{
GasEngine engine = new GasEngine();
engine.Start();
}
}
public class GasEngine
{
public void Start()
{
Console.WriteLine("I use gas as my fuel!");
}
}
Y para crear una instancia de la clase Car usaremos el siguiente código:
Car car = new Car();
El problema con este código es que estamos estrechamente relacionados con GasEngine y si decidimos cambiarlo a ElectricityEngine, tendremos que volver a escribir la clase de automóvil. Y cuanto más grande sea la aplicación, más problemas y dolores de cabeza tendremos que agregar y usar un nuevo tipo de motor.
En otras palabras, con este enfoque es que nuestra clase de Automóviles de alto nivel depende de la clase de GasEngine de nivel inferior que viola el Principio de Inversión de Dependencia (DIP) de SOLID. DIP sugiere que deberíamos depender de abstracciones, no de clases concretas. Para satisfacer esto, introducimos la interfaz de IEngine y reescribimos el código como se muestra a continuación:
public interface IEngine
{
void Start();
}
public class GasEngine : IEngine
{
public void Start()
{
Console.WriteLine("I use gas as my fuel!");
}
}
public class ElectricityEngine : IEngine
{
public void Start()
{
Console.WriteLine("I am electrocar");
}
}
public class Car
{
private readonly IEngine _engine;
public Car(IEngine engine)
{
_engine = engine;
}
public void Run()
{
_engine.Start();
}
}
Ahora nuestra clase Car depende solo de la interfaz de IEngine, no de una implementación específica del motor. Ahora, el único truco es cómo creamos una instancia del Coche y le damos una clase de motor concreta real como GasEngine o ElectricityEngine. Ahí es donde entra la inyección de dependencia .
Car gasCar = new Car(new GasEngine());
gasCar.Run();
Car electroCar = new Car(new ElectricityEngine());
electroCar.Run();
Aquí básicamente inyectamos (pasamos) nuestra dependencia (instancia del motor) al constructor de coches. Así que ahora nuestras clases tienen un acoplamiento suelto entre los objetos y sus dependencias, y podemos agregar fácilmente nuevos tipos de motores sin cambiar la clase de Automóvil.
El principal beneficio de la inyección de dependencia es que las clases se acoplan de forma más flexible, ya que no tienen dependencias codificadas. Esto sigue el principio de inversión de dependencia, que se mencionó anteriormente. En lugar de hacer referencia a implementaciones específicas, las clases solicitan abstracciones (generalmente interfaces ) que se les proporcionan cuando se construye la clase.
Entonces, al final, la inyección de dependencia es solo una técnica para lograr un acoplamiento suelto entre los objetos y sus dependencias. En lugar de instanciar directamente las dependencias que la clase necesita para realizar sus acciones, las dependencias se proporcionan a la clase (la mayoría de las veces) a través de la inyección del constructor.
Además, cuando tenemos muchas dependencias, es una buena práctica utilizar contenedores de Inversión de Control (IOC) que nos permiten indicar qué interfaces deben asignarse a qué implementaciones concretas para todas nuestras dependencias y podemos hacer que las resuelva para nosotros cuando se construye. nuestro objeto Por ejemplo, podríamos especificar en la asignación para el contenedor IoC que la dependencia IEngine se debe asignar a la clase GasEngine y cuando le pedimos al contenedor IoC una instancia de nuestra clase Car , automáticamente construirá nuestra clase Car con una dependencia GasEngine aprobada en.
ACTUALIZACIÓN: He visto recientemente un curso sobre EF Core de Julie Lerman y también le gustó su breve definición sobre DI.
La inyección de dependencia es un patrón para permitir que su aplicación inyecte objetos sobre la marcha en las clases que los necesitan, sin obligar a esas clases a ser responsables de esos objetos. Permite que su código se acople más libremente, y Entity Framework Core se conecta a este mismo sistema de servicios.
This es la explicación más simple sobre la inyección de dependencia y el contenedor de inyección de dependencia que he visto:
Sin inyección de dependencia
- La aplicación necesita Foo (por ejemplo, un controlador), por lo que:
- La aplicación crea Foo
- La aplicación llama a Foo
- Foo necesita Bar (por ejemplo, un servicio), por lo que:
- Foo crea barra
- Foo llama a Bar
- Bar necesita Bim (un servicio, un repositorio, ...), así que:
- Bar crea Bim
- Bar hace algo
Con inyección de dependencia
- La aplicación necesita Foo, que necesita Bar, que necesita Bim, así que:
- La aplicación crea Bim
- La aplicación crea Bar y le da Bim.
- La aplicación crea Foo y le da Bar.
- La aplicación llama a Foo
- Foo llama a Bar
- Bar hace algo
- Foo llama a Bar
Uso de un contenedor de inyección de dependencia
- La aplicación necesita Foo para:
- La aplicación obtiene Foo del contenedor, así que:
- Contenedor crea bim
- Contenedor crea barra y le da bim.
- Contenedor crea Foo y le da Bar.
- La aplicación llama a Foo
- Foo llama a Bar
- Bar hace algo
- Foo llama a Bar
La inyección de dependencias y los contenedores de inyección de dependencias son cosas diferentes:
- La inyección de dependencia es un método para escribir mejor código
- Un contenedor DI es una herramienta para ayudar a inyectar dependencias.
No necesitas un contenedor para hacer inyección de dependencia. Sin embargo un contenedor puede ayudarte.
Inyección de dependencia significa una forma (en realidad de cualquier manera ) para que una parte del código (por ejemplo, una clase) tenga acceso a las dependencias (otras partes del código, por ejemplo, otras clases, de las que depende) de manera modular sin que estén codificadas (así que pueden cambiarse o anularse libremente, o incluso cargarse en otro momento, según sea necesario)
(y ps, sí, se ha convertido en un nombre de $ 25 demasiado exagerado para un concepto bastante simple) , mis .25
centavos
Encontré este ejemplo divertido en términos de acoplamiento suelto :
Cualquier aplicación se compone de muchos objetos que colaboran entre sí para realizar algunas cosas útiles. Tradicionalmente, cada objeto es responsable de obtener sus propias referencias a los objetos dependientes (dependencias) con los que colabora. Esto conduce a clases altamente acopladas y código difícil de probar.
Por ejemplo, considere un objeto de Car
.
Un Car
depende de las ruedas, el motor, el combustible, la batería, etc. para funcionar. Tradicionalmente, definimos la marca de dichos objetos dependientes junto con la definición del objeto Car
.
Sin inyección de dependencia (DI):
class Car{
private Wheel wh = new NepaliRubberWheel();
private Battery bt = new ExcideBattery();
//The rest
}
Aquí, el objeto Car
es responsable de crear los objetos dependientes.
¿Qué NepaliRubberWheel()
si queremos cambiar el tipo de su objeto dependiente, por ejemplo, Wheel
, después de los pinchazos de NepaliRubberWheel()
iniciales? Necesitamos recrear el objeto Car con su nueva dependencia, digamos ChineseRubberWheel()
, pero solo el fabricante del Car
puede hacerlo.
Entonces, ¿para qué nos sirve la Dependency Injection
...?
Cuando se usa la inyección de dependencia, los objetos reciben sus dependencias en tiempo de ejecución en lugar de tiempo de compilación (tiempo de fabricación del automóvil) . Así que ahora podemos cambiar la Wheel
cuando queramos. Aquí, la dependency
( wheel
) se puede inyectar en el Car
en el tiempo de ejecución.
Después de usar la inyección de dependencia:
Aquí, estamos inyectando las dependencias (Rueda y Batería) en tiempo de ejecución. De ahí el término: inyección de dependencia.
class Car{
private Wheel wh = [Inject an Instance of Wheel (dependency of car) at runtime]
private Battery bt = [Inject an Instance of Battery (dependency of car) at runtime]
Car(Wheel wh,Battery bt) {
this.wh = wh;
this.bt = bt;
}
//Or we can have setters
void setWheel(Wheel wh) {
this.wh = wh;
}
}
¿Qué es la inyección de dependencia (DI)?
Como han dicho otros, la inyección de dependencia (DI) elimina la responsabilidad de la creación directa y la administración de la vida útil, de otras instancias de objetos de las que nuestra clase de interés (clase de consumidor) depende (en el sentido de UML ). Estas instancias, en cambio, se pasan a nuestra clase de consumidor, generalmente como parámetros de constructor o mediante establecedores de propiedades (la administración de la creación de instancias de objetos de dependencia y el paso a la clase de consumidor generalmente se realiza mediante un contenedor de Inversión de control (IoC) , pero ese es otro tema) .
DI, DIP y SOLID
Específicamente, en el paradigma de los principios SÓLIDOS de Robert C Martin del Diseño Orientado a Objetos , DI
es una de las implementaciones posibles del Principio de Inversión de Dependencia (DIP) . El DIP es la D
del mantra SOLID
; otras implementaciones de DIP incluyen el Localizador de servicios y los patrones de complementos.
El objetivo del DIP es desacoplar las dependencias concretas y concretas entre clases y, en cambio, aflojar el acoplamiento mediante una abstracción, que se puede lograr a través de una interface
, una abstract class
o pure virtual class
, según el lenguaje y el enfoque utilizado. .
Sin el DIP, nuestro código (he llamado a esta "clase consumidora") se acopla directamente a una dependencia concreta y, a menudo, también tiene la responsabilidad de saber cómo obtener y administrar una instancia de esta dependencia, es decir, conceptualmente:
"I need to create/use a Foo and invoke method `GetBar()`"
Mientras que después de la aplicación del DIP, el requisito se aflojó, y se eliminó la preocupación de obtener y administrar la vida útil de la dependencia de Foo
:
"I need to invoke something which offers `GetBar()`"
¿Por qué usar DIP (y DI)?
El desacoplamiento de las dependencias entre clases de esta manera permite una fácil sustitución de estas clases de dependencia con otras implementaciones que también cumplen con los requisitos previos de la abstracción (por ejemplo, la dependencia se puede cambiar con otra implementación de la misma interfaz). Además, como han mencionado otros, posiblemente la razón más común para desacoplar las clases a través del DIP es permitir que una clase consumidora sea probada de forma aislada, ya que estas mismas dependencias ahora pueden ser tachadas y / o burladas.
Una consecuencia de la ID es que la administración de la vida útil de las instancias de objetos de dependencia ya no está controlada por una clase consumidora, ya que el objeto de dependencia ahora se pasa a la clase consumidora (a través de la inyección del constructor o del definidor).
Esto se puede ver de diferentes maneras:
- Si el control de la vida útil de las dependencias por parte de la clase consumidora debe mantenerse, el control se puede restablecer inyectando una fábrica (abstracta) para crear las instancias de la clase de dependencia en la clase del consumidor. El consumidor podrá obtener instancias a través de
Create
en la fábrica según sea necesario, y deshacerse de estas instancias una vez que esté completo. - O bien, el control de la vida útil de las instancias de dependencia se puede entregar a un contenedor IoC (más información sobre esto más adelante).
¿Cuándo usar DI?
- Cuando sea probable que haya una necesidad de sustituir una dependencia por una implementación equivalente,
- En cualquier momento en el que deba probar por unidad los métodos de una clase en aislamiento de sus dependencias,
- Donde la incertidumbre de la vida útil de una dependencia puede justificar la experimentación (por ejemplo, Hey,
MyDepClass
es segura para las hebras, ¿quéMyDepClass
si lo hacemos un singleton e inyectamos la misma instancia en todos los consumidores?)
Ejemplo
Aquí hay una implementación simple de C #. Teniendo en cuenta la siguiente clase de consumo:
public class MyLogger
{
public void LogRecord(string somethingToLog)
{
Console.WriteLine("{0:HH:mm:ss} - {1}", DateTime.Now, somethingToLog);
}
}
Aunque aparentemente es inocuo, tiene dos dependencias static
en otras dos clases, System.DateTime
y System.Console
, que no solo limitan las opciones de salida de registro (el registro en la consola no tendrá ningún valor si nadie está mirando), pero peor aún, es difícil para probar automáticamente dada la dependencia de un reloj del sistema no determinista.
Sin embargo, podemos aplicar DIP
a esta clase, abstrayendo la preocupación de la marca de tiempo como una dependencia, y MyLogger
solo a una interfaz simple:
public interface IClock
{
DateTime Now { get; }
}
También podemos aflojar la dependencia de la Console
a una abstracción, como un TextWriter
. La inyección de dependencia se implementa normalmente como inyección de constructor
(pasando una abstracción a una dependencia como parámetro al constructor de una clase consumidora) o Setter Injection
(pasando la dependencia a través de un setXyz()
setter o una propiedad .Net con {set;}
definido). Se prefiere la Inyección de constructor, ya que garantiza que la clase estará en un estado correcto después de la construcción, y permite que los campos de dependencia internos se readonly
como de readonly
(C #) o final
(Java). Entonces, al usar la inyección de constructor en el ejemplo anterior, esto nos deja con:
public class MyLogger : ILogger // Others will depend on our logger.
{
private readonly TextWriter _output;
private readonly IClock _clock;
// Dependencies are injected through the constructor
public MyLogger(TextWriter stream, IClock clock)
{
_output = stream;
_clock = clock;
}
public void LogRecord(string somethingToLog)
{
// We can now use our dependencies through the abstraction
// and without knowledge of the lifespans of the dependencies
_output.Write("{0:yyyy-MM-dd HH:mm:ss} - {1}", _clock.Now, somethingToLog);
}
}
(Se debe proporcionar un Clock
concreto, que, por supuesto, podría revertirse a DateTime.Now
, y las dos dependencias deben proporcionarse mediante un contenedor IoC a través de la inyección del constructor)
Se puede construir una prueba de unidad automatizada, lo que demuestra de manera definitiva que nuestro registrador está funcionando correctamente, ya que ahora tenemos control sobre las dependencias, el tiempo y podemos espiar la salida escrita:
[Test]
public void LoggingMustRecordAllInformationAndStampTheTime()
{
// Arrange
var mockClock = new Mock<IClock>();
mockClock.Setup(c => c.Now).Returns(new DateTime(2015, 4, 11, 12, 31, 45));
var fakeConsole = new StringWriter();
// Act
new MyLogger(fakeConsole, mockClock.Object)
.LogRecord("Foo");
// Assert
Assert.AreEqual("2015-04-11 12:31:45 - Foo", fakeConsole.ToString());
}
Próximos pasos
La inyección de dependencia se asocia invariablemente con un contenedor de Inversión de Control (IoC) , para inyectar (proporcionar) las instancias de dependencia concretas y para administrar las instancias de vida útil. Durante el proceso de configuración / arranque, los contenedores IoC
permiten que se defina lo siguiente:
- mapeo entre cada abstracción y la implementación concreta configurada (por ejemplo, "cada vez que un consumidor solicita un
IBar
, devuelve una instancia deConcreteBar
" ) - se pueden configurar políticas para la administración de la vida útil de cada dependencia, por ejemplo, para crear un nuevo objeto para cada instancia de consumidor, para compartir una instancia de dependencia de singleton entre todos los consumidores, para compartir la misma instancia de dependencia solo en el mismo hilo, etc.
- En .Net, los contenedores IoC conocen protocolos como
IDisposable
y asumirán la responsabilidad deDisposing
dependencias de acuerdo con la gestión de la vida útil configurada.
Por lo general, una vez que los contenedores de IoC se han configurado / reiniciado, funcionan sin problemas en segundo plano, lo que permite al programador enfocarse en el código en lugar de preocuparse por las dependencias.
La clave para el código compatible con DI es evitar el acoplamiento estático de clases, y no usar new () para la creación de Dependencias
Según el ejemplo anterior, el desacoplamiento de las dependencias requiere cierto esfuerzo de diseño, y para el desarrollador, se necesita un cambio de paradigma para romper el hábito de crear new
dependencias directamente y, en cambio, confiar en el contenedor para administrar las dependencias.
Pero los beneficios son muchos, especialmente en la capacidad de probar a fondo su clase de interés.
Nota : La creación / cartografía / proyección (a través new ..()
) de POCO / POJO / serialización DTO / Entidad Gráficos / Anonymous JSON proyecciones et al - es decir, "datos sólo" clases o registros - utilizados o devueltos de métodos se no considerados como dependencias (en el Sentido UML) y no sujeto a DI. Usar new
para proyectar esto está bien.
La inyección de dependencia es un tipo de implementación del principio de " Inversión de control " en el que se basa la creación de marcos.
Los marcos como se indica en el "Patrón de diseño" de GoF son clases que implementan la lógica de flujo de control principal, lo que hace que el desarrollador haga eso, de esta manera los marcos se dan cuenta de la inversión del principio de control.
Una forma de implementar como una técnica, y no como una jerarquía de clases, este principio de IoC es simplemente inyección de dependencia.
DI consiste principalmente en delegar la asignación de instancias de clases y la referencia de tipo a esas instancias, a una "entidad" externa: un objeto, una clase estática, un componente, un marco, etc.
Las instancias de clases son las " dependencias ", el enlace externo del componente que llama con la instancia de clase a través de la referencia es la " inyección ".
Obviamente, puede implementar esta técnica de la forma que desee desde el punto de vista de la POO, vea, por ejemplo , inyección de constructor , inyección de definidor , inyección de interfaz .
Delegar a un tercero para llevar a cabo la tarea de hacer coincidir una referencia con un objeto es muy útil cuando se desea separar completamente un componente que necesita algunos servicios de la misma implementación de servicios.
De esta manera, al diseñar componentes, puede concentrarse exclusivamente en su arquitectura y su lógica específica, confiando en las interfaces para colaborar con otros objetos sin preocuparse por ningún tipo de cambios de implementación de los objetos / servicios utilizados, también si el mismo objeto que está utilizando Será totalmente reemplazado (obviamente respetando la interfaz).
Todas las respuestas anteriores son buenas, mi objetivo es explicar el concepto de una manera sencilla para que cualquier persona sin conocimientos de programación también pueda entender el concepto.
La inyección de dependencia es uno de los patrones de diseño que nos ayudan a crear sistemas complejos de una manera más sencilla.
Podemos ver una gran variedad de aplicaciones de este patrón en nuestro día a día. Algunos de los ejemplos son grabadora de cinta, VCD, unidad de CD, etc.
La imagen de arriba es una imagen de una grabadora de cinta portátil de carrete a carrete, de mediados del siglo XX. Source .
La intención principal de una máquina grabadora de cinta es grabar o reproducir sonido. Mientras se diseña un sistema, se necesita un carrete para grabar o reproducir sonido o música. Podemos colocar el carrete dentro de la máquina o podemos proporcionar un gancho para el carrete donde se puede colocar. Si optamos por el segundo que coloca un gancho para el carrete, obtenemos un beneficio adicional de reproducir cualquier música cambiando el carrete y también reduciendo la función de reproducción de lo que sea en el carrete.
Los principales beneficios que conseguimos al utilizar la inyección de dependencia.
- Alta cohesión y acoplamiento suelto.
- Externalizando la dependencia y buscando solo la responsabilidad.
- Haciendo cosas como componentes y combinándolos para formar un gran sistema con altas capacidades.
- Ayuda a desarrollar componentes de alta calidad, ya que se desarrollan de forma independiente y se prueban adecuadamente.
- Ayuda a reemplazar el componente con otro si uno falla.
Hoy en día, este concepto forma la base de marcos bien conocidos en el mundo de la programación. Los Spring Angular, etc. son los marcos de software conocidos que se basan en este concepto.
La inyección de dependencia es un patrón que se utiliza para crear instancias de objetos en los que otros objetos dependen sin saber en el momento de la compilación qué clase se usará para proporcionar esa funcionalidad o simplemente la forma de inyectar propiedades a un objeto se llama inyección de dependencia.
Ejemplo para inyección de dependencia
Anteriormente estamos escribiendo código como este.
Public MyClass{
DependentClass dependentObject
/*
At somewhere in our code we need to instantiate
the object with new operator inorder to use it or perform some method.
*/
dependentObject= new DependentClass();
dependentObject.someMethod();
}
Con la inyección de dependencia, el inyector de dependencia tomará la instanciación para nosotros
Public MyClass{
/* Dependency injector will instantiate object*/
DependentClass dependentObject
/*
At somewhere in our code we perform some method.
The process of instantiation will be handled by the dependency injector
*/
dependentObject.someMethod();
}
También puedes leer
Diferencia entre inversión de control e inyección de dependencia
Creo que ya que todos han escrito para DI, permítanme hacer algunas preguntas.
- Cuando tiene una configuración de DI donde todas las implementaciones reales (no interfaces) que se van a inyectar en una clase (para, por ejemplo, servicios a un controlador), ¿por qué no es una especie de codificación?
- ¿Qué pasa si quiero cambiar el objeto en tiempo de ejecución? Por ejemplo, mi configuración ya dice cuando instalo MyController, inyectar para FileLogger como ILogger. Pero me gustaría inyectar DatabaseLogger.
- Cada vez que quiero cambiar los objetos que necesita mi clase de clase, ahora debo buscar en dos lugares: la clase en sí y el archivo de configuración. ¿Cómo hace eso la vida más fácil?
- Si Aproperty of AClass no se inyecta, ¿es más difícil burlarse de él?
- Volviendo a la primera pregunta. Si el uso de new object () es malo, ¿por qué inyectamos la implementación y no la interfaz? Creo que muchos de ustedes están diciendo que, de hecho, estamos inyectando la interfaz, pero la configuración hace que especifique la implementación de esa interfaz ... no en tiempo de ejecución ... está codificada en el proceso de compilación.
Esto se basa en la respuesta que @Adam N publicó.
¿Por qué PersonService ya no tiene que preocuparse por GroupMembershipService? Usted acaba de mencionar que GroupMembership tiene varias cosas (objetos / propiedades) de las que depende. Si GMService fuera requerido en PService, lo tendría como propiedad. Puedes burlarte de eso sin importar si lo inyectaste o no. La única vez que me gustaría que se inyectara es si GMService tuviera clases secundarias más específicas, que no sabría hasta el tiempo de ejecución. Entonces querrías inyectar la subclase. O si quieres usar eso como singleton o prototipo. Para ser honesto, el archivo de configuración tiene un código completo en cuanto a qué subclase para un tipo (interfaz) que va a inyectar durante el tiempo de compilación.
EDITAR
Un bonito comentario de Jose Maria Arranz en DI
DI aumenta la cohesión al eliminar cualquier necesidad de determinar la dirección de la dependencia y escribir cualquier código de pegamento.
Falso. La dirección de las dependencias es en formato XML o como anotaciones, sus dependencias se escriben como código XML y anotaciones. XML y anotaciones son código fuente.
DI reduce el acoplamiento al hacer que todos sus componentes sean modulares (es decir, reemplazables) y que tengan interfaces bien definidas entre sí.
Falso. No necesita un marco DI para construir un código modular basado en interfaces.
Acerca de reemplazables: con un archivo .properties muy simple y Class.forName puede definir qué clases pueden cambiar. Si CUALQUIER clase de su código se puede cambiar, Java no es para usted, use un lenguaje de scripting. Por cierto: las anotaciones no se pueden cambiar sin volver a compilar.
En mi opinión, hay una única razón para los marcos DI: reducción de la placa de la caldera. Con un sistema de fábrica bien hecho, puede hacer lo mismo, más controlado y más predecible que su marco DI preferido, los marcos DI prometen una reducción del código (XML y las anotaciones también son el código fuente). El problema es que la reducción de la placa de la caldera es real en casos muy simples (una instancia por clase y similares), a veces en el mundo real, elegir el objeto de servicio apropiado no es tan fácil como asignar una clase a un objeto único.
La inyección de dependencia es el núcleo del concepto relacionado con Spring Framework. Mientras se crea el marco de cualquier proyecto, la primavera puede desempeñar un papel vital, y aquí la inyección de dependencia se presenta en el lanzador.
En realidad, supongamos que en java creaste dos clases diferentes como clase A y clase B, y cualquiera que sea la función disponible en la clase B que quieras usar en la clase A, entonces en ese momento se puede usar la inyección de dependencia. donde puede crear objetos de una clase en otra, de la misma manera que puede inyectar una clase completa en otra clase para hacerla accesible. De esta manera se puede superar la dependencia.
LA INYECCIÓN DE DEPENDENCIA ES SIMPLEMENTE ENCONTRAR DOS CLASES Y AL MISMO TIEMPO DE MANTENERLAS POR SEPARADO.
De The Book, '' Well-Grounded Java Developer: Técnicas vitales de Java 7 y programación políglota
DI es una forma particular de IoC, por lo que el proceso de encontrar sus dependencias está fuera del control directo de su código que se está ejecutando actualmente.
Ejemplo, tenemos 2 clases Client
y Service
. Client
utilizaráService
public class Service {
public void doSomeThingInService() {
// ...
}
}
Sin inyección de dependencia
Camino 1)
public class Client {
public void doSomeThingInClient() {
Service service = new Service();
service.doSomeThingInService();
}
}
Camino 2)
public class Client {
Service service = new Service();
public void doSomeThingInClient() {
service.doSomeThingInService();
}
}
Camino 3)
public class Client {
Service service;
public Client() {
service = new Service();
}
public void doSomeThingInClient() {
service.doSomeThingInService();
}
}
1) 2) 3) Usando
Client client = new Client();
client.doSomeThingInService();
Ventajas
- Sencillo
Desventajas
- Duro para la
Client
clase de prueba - Cuando cambiamos de
Service
constructor, necesitamos cambiar el código en todo lugar crearService
objeto
Usar inyección de dependencia
Manera 1) Inyección de constructor.
public class Client {
Service service;
Client(Service service) {
this.service = service;
}
// Example Client has 2 dependency
// Client(Service service, IDatabas database) {
// this.service = service;
// this.database = database;
// }
public void doSomeThingInClient() {
service.doSomeThingInService();
}
}
Utilizando
Client client = new Client(new Service());
// Client client = new Client(new Service(), new SqliteDatabase());
client.doSomeThingInClient();
Manera 2) inyección de Setter
public class Client {
Service service;
public void setService(Service service) {
this.service = service;
}
public void doSomeThingInClient() {
service.doSomeThingInService();
}
}
Utilizando
Client client = new Client();
client.setService(new Service());
client.doSomeThingInClient();
Manera 3) inyección de interfaz
Compruebe https://en.wikipedia.org/wiki/Dependency_injection
===
Ahora, este código ya se sigue Dependency Injection
y es más fácil para la Client
clase de prueba .
Sin embargo, todavía usamos new Service()
mucho tiempo y no es bueno cuando cambiamos de Service
constructor. Para evitarlo, podemos usar el inyector DI como
1) Manual simpleInjector
public class Injector {
public static Service provideService(){
return new Service();
}
public static IDatabase provideDatatBase(){
return new SqliteDatabase();
}
public static ObjectA provideObjectA(){
return new ObjectA(provideService(...));
}
}
Utilizando
Service service = Injector.provideService();
2) Usar biblioteca: para dagger2 Androiddagger2
Ventajas
- Facilita la prueba
- Cuando cambia el
Service
, solo necesita cambiarlo en la clase Injector - Si usa el uso
Constructor Injection
, cuando mire al constructor deClient
, verá cuánta dependencia deClient
clase
Desventajas
- Si usa el uso
Constructor Injection
, elService
objeto se crea cuando seClient
crea, en algún momento usamos la función en laClient
clase sin uso,Service
porService
lo que el desperdicio creado se desperdicia
Definición de inyección de dependencia
https://en.wikipedia.org/wiki/Dependency_injection
Una dependencia es un objeto que se puede usar (
Service
)
Una inyección es el paso de una dependencia (Service
) a un objeto dependiente (Client
) que la usaría
El objetivo principal de la inyección de dependencia (DI) es mantener el código fuente de la aplicación limpio y estable :
- Limpio de código de inicialización de dependencia.
- estable independientemente de la dependencia utilizada
Prácticamente, cada patrón de diseño separa las preocupaciones para que los cambios futuros afecten a los archivos mínimos.
El dominio específico de DI es la delegación de configuración de la dependencia y la inicialización.
Ejemplo: DI con shell script
Si ocasionalmente trabaja fuera de Java, recuerde cómo source
se usa a menudo en muchos lenguajes de script (Shell, Tcl, etc., o incluso import
en Python, mal usado para este propósito).
Considere un dependent.sh
script simple :
#!/bin/sh
# Dependent
touch "one.txt" "two.txt"
archive_files "one.txt" "two.txt"
El script es dependiente: no se ejecutará correctamente solo ( archive_files
no está definido).
Se define archive_files
en el archive_files_zip.sh
script de implementación (utilizando zip
en este caso):
#!/bin/sh
# Dependency
function archive_files {
zip files.zip "$@"
}
En lugar de source
utilizar el script de implementación directamente en el dependiente, utiliza un injector.sh
"contenedor" que envuelve ambos "componentes":
#!/bin/sh
# Injector
source ./archive_files_zip.sh
source ./dependent.sh
La archive_files
dependencia acaba de ser inyectada en el script dependiente .
Podrías haber inyectado la dependencia que implementa archive_files
usando tar
o xz
.
Ejemplo: eliminar DI
Si el dependent.sh
script usara las dependencias directamente, el enfoque se llamaría búsqueda de dependencia (que es opuesto a la inyección de dependencia ):
#!/bin/sh
# Dependent
# dependency look-up
source ./archive_files_zip.sh
touch "one.txt" "two.txt"
archive_files "one.txt" "two.txt"
Ahora el problema es que el "componente" dependiente tiene que realizar la inicialización.
El código fuente del "componente" no es limpio ni estable porque cada cambio en la inicialización de las dependencias requiere una nueva versión para el archivo de código fuente de los "componentes"
Ultimas palabras
DI no se enfatiza ni se populariza tanto como en los marcos de Java.
Pero es un enfoque genérico para dividir las preocupaciones de:
- desarrollo de aplicaciones ( ciclo de vida de lanzamiento de código fuente único )
- despliegue de aplicaciones ( múltiples entornos de destino con ciclos de vida independientes)
El uso de la configuración solo con la búsqueda de dependencia no ayuda, ya que la cantidad de parámetros de configuración puede cambiar por dependencia (por ejemplo, el nuevo tipo de autenticación), así como la cantidad de tipos de dependencias compatibles (por ejemplo, el nuevo tipo de base de datos).
En palabras simples, la inyección de dependencia (DI) es la forma de eliminar las dependencias o el acoplamiento apretado entre diferentes objetos. La inyección de dependencia da un comportamiento cohesivo a cada objeto.
DI es la implementación del director de IOC de Spring que dice "No nos llames, te llamaremos". El uso del programador de inyección de dependencias no necesita crear un objeto con la nueva palabra clave.
Los objetos una vez se cargan en el contenedor de Spring y luego los reutilizamos cuando los necesitamos al buscarlos desde el contenedor de Spring utilizando el método getBean (String beanName).
Hacer que el concepto de inyección de dependencia sea fácil de entender. Tomemos un ejemplo de botón de encendido para encender (on / off) una bombilla.
Sin inyección de dependencia
El conmutador necesita saber de antemano a qué bombilla estoy conectado (dependencia codificada). Asi que,
Interruptor -> PermanentBulb // el interruptor está directamente conectado a la bombilla permanente, no es posible realizar pruebas fácilmente
Switch(){
PermanentBulb = new Bulb();
PermanentBulb.Toggle();
}
Con inyección de dependencia
Solo el interruptor sabe que necesito encender / apagar cualquier bombilla que se me pase. Asi que,
Interruptor -> Bulb1 O Bulb2 O NightBulb (dependencia inyectada)
Switch(AnyBulb){ //pass it whichever bulb you like
AnyBulb.Toggle();
}
Modificando el ejemplo de James para el interruptor y la bombilla:
public class SwitchTest {
TestToggleBulb() {
MockBulb mockbulb = new MockBulb();
// MockBulb is a subclass of Bulb, so we can
// "inject" it here:
Switch switch = new Switch(mockBulb);
switch.ToggleBulb();
mockBulb.AssertToggleWasCalled();
}
}
public class Switch {
private Bulb myBulb;
public Switch() {
myBulb = new Bulb();
}
public Switch(Bulb useThisBulbInstead) {
myBulb = useThisBulbInstead;
}
public void ToggleBulb() {
...
myBulb.Toggle();
...
}
}`
La inyección de dependencia (DI) es parte de la práctica del Principio de inversión de dependencia (DIP), que también se denomina Inversión de control (IoC). Básicamente, necesita hacer DIP porque desea que su código sea más modular y comprobable por unidad, en lugar de un solo sistema monolítico. Entonces, comienza a identificar partes del código que pueden separarse de la clase y abstraerse. Ahora la implementación de la abstracción debe inyectarse desde fuera de la clase. Normalmente esto se puede hacer a través del constructor. Así que creas un constructor que acepta la abstracción como un parámetro, y esto se llama inyección de dependencia (a través del constructor). Para obtener más información sobre el contenedor DIP, DI e IoC, puede leer Here
La inyección de dependencia (DI) es una de los patrones de diseño, que utiliza la característica básica de la POO: la relación en un objeto con otro objeto. Mientras que la herencia hereda un objeto para hacer más complejo y específico otro objeto, la relación o asociación simplemente crea un puntero a otro objeto desde un objeto usando un atributo. El poder de DI está en combinación con otras características de OOP, como son las interfaces y el código de ocultación. Supongamos que tenemos un cliente (suscriptor) en la biblioteca, que solo puede pedir prestado un libro por simplicidad.
Interfaz de libro:
package com.deepam.hidden;
public interface BookInterface {
public BookInterface setHeight(int height);
public BookInterface setPages(int pages);
public int getHeight();
public int getPages();
public String toString();
}
A continuación podemos tener muchos tipos de libros; uno de tipo es la ficción:
package com.deepam.hidden;
public class FictionBook implements BookInterface {
int height = 0; // height in cm
int pages = 0; // number of pages
/** constructor */
public FictionBook() {
// TODO Auto-generated constructor stub
}
@Override
public FictionBook setHeight(int height) {
this.height = height;
return this;
}
@Override
public FictionBook setPages(int pages) {
this.pages = pages;
return this;
}
@Override
public int getHeight() {
// TODO Auto-generated method stub
return height;
}
@Override
public int getPages() {
// TODO Auto-generated method stub
return pages;
}
@Override
public String toString(){
return ("height: " + height + ", " + "pages: " + pages);
}
}
Ahora el suscriptor puede tener asociación al libro:
package com.deepam.hidden;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Subscriber {
BookInterface book;
/** constructor*/
public Subscriber() {
// TODO Auto-generated constructor stub
}
// injection I
public void setBook(BookInterface book) {
this.book = book;
}
// injection II
public BookInterface setBook(String bookName) {
try {
Class<?> cl = Class.forName(bookName);
Constructor<?> constructor = cl.getConstructor(); // use it for parameters in constructor
BookInterface book = (BookInterface) constructor.newInstance();
//book = (BookInterface) Class.forName(bookName).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return book;
}
public BookInterface getBook() {
return book;
}
public static void main(String[] args) {
}
}
Las tres clases se pueden ocultar para su propia implementación. Ahora podemos usar este código para DI:
package com.deepam.implement;
import com.deepam.hidden.Subscriber;
import com.deepam.hidden.FictionBook;
public class CallHiddenImplBook {
public CallHiddenImplBook() {
// TODO Auto-generated constructor stub
}
public void doIt() {
Subscriber ab = new Subscriber();
// injection I
FictionBook bookI = new FictionBook();
bookI.setHeight(30); // cm
bookI.setPages(250);
ab.setBook(bookI); // inject
System.out.println("injection I " + ab.getBook().toString());
// injection II
FictionBook bookII = ((FictionBook) ab.setBook("com.deepam.hidden.FictionBook")).setHeight(5).setPages(108); // inject and set
System.out.println("injection II " + ab.getBook().toString());
}
public static void main(String[] args) {
CallHiddenImplBook kh = new CallHiddenImplBook();
kh.doIt();
}
}
Hay muchas maneras diferentes de cómo usar la inyección de dependencia. Es posible combinarlo con Singleton, etc., pero aún en básico solo se realiza asociación creando atributos de tipo de objeto dentro de otro objeto. La utilidad es única y única en función, ese código, que deberíamos escribir una y otra vez, siempre está preparado y hecho para nosotros. Esta es la razón por la que DI está tan estrechamente vinculada con la Inversión de Control (IoC), lo que significa que nuestro programa pasa a controlar otro módulo en ejecución, que inyecta beans a nuestro código. (Cada objeto, que se puede inyectar, se puede firmar o considerar como un Bean). Por ejemplo, en Spring se realiza creando e inicializando ApplicationContextcontenedor, que hace este trabajo para nosotros. Simplemente, en nuestro código creamos el contexto e invocamos la inicialización de los beans. En ese momento la inyección se ha realizado automáticamente.
La inyección de dependencia es una solución posible a lo que generalmente se denomina requisito de "ofuscación de dependencia". La ofuscación por dependencia es un método para eliminar la naturaleza "obvia" del proceso de proporcionar una dependencia a una clase que lo requiere y, por lo tanto, ofuscar, de alguna manera, la provisión de dicha dependencia a dicha clase. Este no es necesariamente algo malo. De hecho, al ofuscar la manera en que se proporciona una dependencia a una clase, entonces algo fuera de la clase es responsable de crear la dependencia, lo que significa que, en varios escenarios, se puede proporcionar una implementación diferente de la dependencia a la clase sin realizar ningún cambio. a la clase. Esto es excelente para cambiar entre los modos de producción y prueba (por ejemplo, usar una dependencia de servicio ''simulada'').
Desafortunadamente, lo malo es que algunas personas han asumido que usted necesita un marco especializado para realizar la ofuscación de dependencia y que de alguna manera usted es un programador ''menor'' si decide no usar un marco particular para hacerlo. Otro mito extremadamente perturbador, que muchos creen, es que la inyección de dependencia es la única forma de lograr la ofuscación de dependencia. Esto es demostrable e históricamente y obviamente 100% incorrecto, pero tendrá problemas para convencer a algunas personas de que existen alternativas a la inyección de dependencia para sus requisitos de ofuscación de dependencia.
Los programadores han comprendido el requisito de ofuscación de dependencia durante años y muchas soluciones alternativas han evolucionado antes y después de que se creara la inyección de dependencia. Existen patrones de fábrica, pero también hay muchas opciones con ThreadLocal en las que no se necesita inyección para una instancia en particular: la dependencia se inyecta efectivamente en el hilo, lo que tiene la ventaja de hacer que el objeto esté disponible (a través de métodos de obtención estática convenientes) para cualquierclase que lo requiere sin tener que agregar anotaciones a las clases que lo requieren y configurar el intrincado ''pegamento'' de XML para hacerlo realidad. Cuando sus dependencias son necesarias para la persistencia (JPA / JDO o lo que sea), le permite lograr una "persistencia transparente" mucho más fácil y con el modelo de dominio y las clases de modelo de negocio compuestas exclusivamente de POJO (es decir, no hay un marco específico / bloqueado en las anotaciones).
La mejor analogía que se me ocurre es el cirujano y su asistente (s) en un quirófano, donde el cirujano es la persona principal y su asistente, que proporciona los diversos componentes quirúrgicos cuando los necesita para que el cirujano pueda concentrarse en el Lo que él hace mejor (cirugía). Sin el asistente, el cirujano tiene que obtener los componentes por sí mismo cada vez que necesita uno.
DI, para abreviar, es una técnica para eliminar una responsabilidad adicional común (carga) sobre los componentes para obtener los componentes dependientes, proporcionándolos.
DI te acerca al principio de responsabilidad única (SR), como el surgeon who can concentrate on surgery
.
Cuándo usar DI: recomendaría usar DI en casi todos los proyectos de producción (pequeños / grandes), especialmente en entornos empresariales en constante cambio :)
Por qué: porque desea que su código sea fácilmente comprobable, simulable, etc. para que pueda probar rápidamente sus cambios y llevarlo al mercado. Además, ¿por qué no lo harías cuando hay un montón de herramientas / marcos gratuitos increíbles para ayudarte en tu viaje a una base de código donde tienes más control?
Las respuestas populares son inútiles, porque definen la inyección de dependencia de una manera que no es útil. Acordemos que por "dependencia" nos referimos a algún otro objeto preexistente que nuestro objeto X necesita. Pero no decimos que estamos haciendo "inyección de dependencia" cuando decimos
$foo = Foo->new($bar);
Simplemente llamamos a los parámetros que pasan al constructor. Lo hemos estado haciendo regularmente desde que se inventaron los constructores.
La "inyección de dependencia" se considera un tipo de "inversión de control", lo que significa que se saca algo de lógica de la persona que llama. Ese no es el caso cuando la persona que llama pasa los parámetros, por lo que si fuera DI, DI no implicaría una inversión de control.
DI significa que hay un nivel intermedio entre el llamante y el constructor que gestiona las dependencias. Un Makefile es un ejemplo simple de inyección de dependencia. El "llamador" es la persona que escribe "make bar" en la línea de comandos, y el "constructor" es el compilador. El Makefile especifica que la barra depende de foo, y hace un
gcc -c foo.cpp; gcc -c bar.cpp
antes de hacer un
gcc foo.o bar.o -o bar
La persona que escribe "make bar" no necesita saber que la barra depende de foo. La dependencia se inyectó entre "make bar" y gcc.
El propósito principal del nivel intermedio no es solo pasar las dependencias al constructor, sino listar todas las dependencias en un solo lugar y ocultarlas del codificador (no hacer que el codificador las proporcione).
Generalmente, el nivel intermedio proporciona fábricas para los objetos construidos, que deben proporcionar un rol que cada tipo de objeto solicitado debe cumplir. Esto se debe a que al tener un nivel intermedio que oculta los detalles de la construcción, ya incurrió en la penalización de extracción impuesta por las fábricas, por lo que también podría utilizar fábricas.
Propondría una definición ligeramente diferente, breve y precisa de qué es la Inyección de Dependencia, centrándose en el objetivo principal, no en los medios técnicos (siguiendo de here adelante ):
La inyección de dependencia es el proceso de crear el gráfico estático y sin estado de los objetos de servicio, donde cada servicio está parametrizado por sus dependencias.
Los objetos que creamos en nuestras aplicaciones (independientemente de si usamos Java, C # u otro lenguaje orientado a objetos) generalmente caen en una de dos categorías: "objetos de servicio" (módulos) sin estado, estáticos y globales, y con estado, dinámico y local “Objetos de datos”.
El gráfico del módulo, el gráfico de los objetos de servicio, se crea normalmente en el inicio de la aplicación. Esto se puede hacer usando un contenedor, como Spring, pero también se puede hacer manualmente, pasando parámetros a los constructores de objetos. Ambas formas tienen sus pros y sus contras, pero definitivamente no es necesario un marco para usar DI en su aplicación.
Un requisito es que los servicios deben estar parametrizados por sus dependencias. Lo que esto significa depende exactamente del lenguaje y el enfoque adoptado en un sistema determinado. Generalmente, esto toma la forma de parámetros de constructor, pero el uso de configuradores también es una opción. Esto también significa que las dependencias de un servicio están ocultas (cuando se invoca un método de servicio) de los usuarios del servicio.
¿Cuándo usar? Yo diría que siempre que la aplicación sea lo suficientemente grande como para encapsular la lógica en módulos separados, con un gráfico de dependencia entre los módulos se obtiene una mayor legibilidad y capacidad de exploración del código.
Sé que ya hay muchas respuestas, pero esto me resultó muy útil: http://tutorials.jenkov.com/dependency-injection/index.html
Sin dependencia:
public class MyDao {
protected DataSource dataSource =
new DataSourceImpl("driver", "url", "user", "password");
//data access methods...
public Person readPerson(int primaryKey) {...}
}
Dependencia:
public class MyDao {
protected DataSource dataSource = null;
public MyDao(String driver, String url, String user, String
password){
this.dataSource = new DataSourceImpl(driver, url, user, password);
}
//data access methods...
public Person readPerson(int primaryKey)
{...}
}
Observe cómo la DataSourceImpl
creación de instancias se mueve a un constructor. El constructor toma cuatro parámetros que son los cuatro valores que necesita el DataSourceImpl
. Aunque la MyDao
clase aún depende de estos cuatro valores, ya no satisface estas dependencias. Son proporcionados por cualquier clase creando una MyDao
instancia.
Significa que los objetos solo deben tener tantas dependencias como sea necesario para hacer su trabajo y las dependencias deben ser pocas. Además, las dependencias de un objeto deben estar en interfaces y no en objetos "concretos", cuando sea posible. (Un objeto concreto es cualquier objeto creado con la palabra clave nueva). El acoplamiento suelto promueve una mayor reutilización, un mantenimiento más fácil y le permite proporcionar fácilmente objetos "simulados" en lugar de servicios costosos.
La "inyección de dependencia" (DI) también se conoce como "Inversión de control" (IoC), y se puede utilizar como una técnica para fomentar este acoplamiento suelto.
Hay dos enfoques principales para implementar DI:
- Inyección de constructor
- Inyección de Setter
Inyección de constructor
Es la técnica de pasar objetos dependientes a su constructor.
Tenga en cuenta que el constructor acepta una interfaz y no un objeto concreto. Además, tenga en cuenta que se lanza una excepción si el parámetro orderDao es nulo. Esto enfatiza la importancia de recibir una dependencia válida. La Inyección del constructor es, en mi opinión, el mecanismo preferido para dar a un objeto sus dependencias. Es claro para el desarrollador al invocar el objeto las dependencias que deben darse al objeto "Persona" para su correcta ejecución.
Inyección de Setter
Pero considere el siguiente ejemplo ... Suponga que tiene una clase con diez métodos que no tienen dependencias, pero está agregando un nuevo método que sí tiene una dependencia en IDAO. Puede cambiar el constructor para usar la Inyección de constructor, pero esto puede forzarle a realizar cambios en todas las llamadas de constructor en todo el lugar. Alternativamente, podría agregar un nuevo constructor que tome la dependencia, pero luego, ¿cómo puede un desarrollador saber cuándo usar un constructor sobre el otro? Finalmente, si la dependencia es muy costosa de crear, ¿por qué debería crearse y pasarse al constructor cuando solo se puede usar raramente? La "inyección de Setter" es otra técnica de DI que puede usarse en situaciones como esta.
La inyección de Setter no obliga a que las dependencias se pasen al constructor. En su lugar, las dependencias se establecen en propiedades públicas expuestas por el objeto que se necesita. Como se indicó anteriormente, los principales motivadores para hacer esto incluyen:
- Apoyar la inyección de dependencias sin tener que modificar el constructor de una clase heredada.
- Permitiendo que se creen recursos o servicios costosos lo más tarde posible y solo cuando sea necesario.
Este es el ejemplo de cómo se vería el código anterior:
public class Person {
public Person() {}
public IDAO Address {
set { addressdao = value; }
get {
if (addressdao == null)
throw new MemberAccessException("addressdao" +
" has not been initialized");
return addressdao;
}
}
public Address GetAddress() {
// ... code that uses the addressdao object
// to fetch address details from the datasource ...
}
// Should not be called directly;
// use the public property instead
private IDAO addressdao;
del libro Apress.Spring.Persistence.with.Hibernate.Oct.2010
El propósito de la inyección de dependencia es desacoplar el trabajo de resolver componentes de software externos de la lógica empresarial de su aplicación. Sin inyección de dependencia, los detalles de cómo un componente accede a los servicios requeridos pueden confundirse con el código del componente. Esto no solo aumenta el potencial de errores, aumenta el código y aumenta las complejidades de mantenimiento; une los componentes más estrechamente, lo que dificulta la modificación de las dependencias al refactorizar o probar.