¿Cuál es la mejor definición para la inyección de dependencia?

10

Cada vez que alguien me alcanza y me pide que defina la Inyección de dependencia de una manera conceptual y explique los pros y los contras reales del uso de DI en el diseño de software. Confieso que tengo algunas dificultades para explicar los conceptos de DI. Cada vez que necesito contarles la historia sobre el principio de responsabilidad única, la composición sobre la herencia, etc.

¿Alguien puede ayudarme a explicar la mejor manera de describir la DI para los desarrolladores?

Tiago Sampaio
fuente
2
El desafío aquí es que hay tantas definiciones conflictivas de DI. Tomo la postura de "DI puro": si tengo una función que se basa en sus parámetros para proporcionar todos los estados, datos, etc., entonces esa función está usando DI. En el otro extremo, algunos argumentarán que sin un marco DI, no hay inyección de dependencia (aunque, por supuesto, están equivocados). Entonces, a menos que puedas definir una definición, no puedes comenzar a explicar qué es ...
David Arno
Entonces, según tengo entendido, este no es solo un problema mío.
Tiago Sampaio
Todo se reduce a esto: la inyección de dependencia es una técnica utilizada para lograr la inversión de dependencia; todo lo demás es solo material extra construido encima de eso Tenga en cuenta que en estos dos términos la palabra "dependencia" tiene significados ligeramente diferentes. En la inyección de dependencia, se refiere al componente del que depende el código. En inversión de dependencia, se refiere a la relación (dirigida) en sí, la que queremos invertir. El último es el objetivo, por lo que los principales pros y contras son los mismos; además de algunas preocupaciones adicionales relacionadas con la implementación real, como la administración de la vida útil del objeto.
Filip Milovanović

Respuestas:

22

La inyección de dependencia es un nombre horrible (IMO) 1 para un concepto bastante sencillo. Aquí hay un ejemplo:

  1. Tiene un método (o clase con métodos) que hace X (por ejemplo, recuperar datos de la base de datos)
  2. Como parte de hacer X, dicho método crea y gestiona un recurso interno (por ejemplo, a DbContext). Este recurso interno es lo que se llama dependencia
  3. Elimina la creación y la administración del recurso (es decir DbContext) del método y hace que la persona que llama sea responsable de proporcionar este recurso (como un parámetro del método o al crear una instancia de la clase)
  4. Ahora está haciendo una inyección de dependencia.


[1] : vengo de un nivel inferior y me llevó meses sentarme y aprender la inyección de dependencia porque el nombre implica que sería algo mucho más complicado, como la inyección de DLL . El hecho de que Visual Studio (y nosotros los desarrolladores en general) se refiera a las bibliotecas .NET (DLL o ensamblados ) de las que depende un proyecto como dependencias no ayuda en absoluto. Incluso existe el Dependency Walker (depe.exe) .


[Editar] Supuse que algunos códigos de demostración serían útiles para algunos, así que aquí hay uno (en C #).

Sin inyección de dependencia:

public class Repository : IDisposable
{
    protected DbContext Context { get; }

    public Repository()
    {
        Context = new DbContext("name=MyEntities");
    }

    public void Dispose()
    {
        Context.Dispose();
    }
}

Su consumidor entonces haría algo como:

using ( var repository = new Repository() )
{
    // work
}

La misma clase implementada con el patrón de inyección de dependencia sería así:

public class RepositoryWithDI
{
    protected DbContext Context { get; }

    public RepositoryWithDI(DbContext context)
    {
        Context = context;
    }
}

Ahora es responsabilidad de quien llama crear una instancia DbContexty pasarla (errm, inyectarla ) a su clase:

using ( var context = new DbContext("name=MyEntities") )
{
    var repository = new RepositoryWithDI(context);

    // work
}
Marc.2377
fuente
3
Esto debería agregarse a Wikipedia.
Evorlor
2
Ahora es responsabilidad de quien llama crear una instancia de un DbContext ; creo que esto sería responsabilidad del punto de entrada de la aplicación para crear instancias de todas las dependencias requeridas. Por lo tanto, el consumidor solo necesita introducir el tipo requerido como una dependencia en el propio contrato.
Fabio
@Fabio Podría ser. (En ese caso, la responsabilidad de la persona que llama sería proporcionar el recurso que fue instanciado en el inicio de la aplicación al método / clase que se está llamando). Sin embargo, en mi ejemplo, no lo es, ya que no es un requisito para explicar el concepto de inyección de dependencia .
Marc.2377
5

Los conceptos abstractos a menudo se explican mejor utilizando una analogía del mundo real. Esta es mi analogía:

Tienes una tienda de sándwiches. Haces sándwiches increíbles, pero sabes poco o nada sobre el pan en sí. Solo tienes pan blanco soso. Tu trabajo se enfoca completamente en los ingredientes que usas para convertir el pan en un sándwich.

Sin embargo, algunos de sus clientes realmente preferirían el pan integral. Algunos preferirían los cereales integrales. Realmente no te importa, puedes hacer cualquier sándwich increíble siempre que sea un pan de tamaño similar. Tampoco querrás tener que asumir la responsabilidad adicional de adquirir varios tipos de pan y mantener las existencias. Incluso si almacena varios tipos de pan, siempre habrá algún cliente con un sabor exótico en el pan que no podría prever razonablemente.

Entonces instituyes una nueva regla: los clientes traen su propio pan. Ya no proporciona pan usted mismo. Esta es una situación en la que todos ganan: los clientes obtienen el pan exacto que desean y ya no tienen que preocuparse por adquirir el pan que no les interesa. Después de todo, eres un fabricante de sándwiches, no un panadero.

Ah, y para acomodar a aquellos clientes que no quieren comprar su propio pan, abres una segunda tienda al lado que vende tus panes blancos suaves originales. Los clientes que no trajeron su propio pan simplemente deben obtener el predeterminado y luego acudir a usted para hacer un sándwich con él.

No es perfecto, pero destaca la característica clave: dar control al consumidor . El beneficio mutuo inherente es que ya no tiene que adquirir sus propias dependencias, y su consumidor no se ve obstaculizado en su elección de dependencia.

Flater
fuente
1
Me gusta esto, pero el OP está buscando una explicación para los desarrolladores . Una abstracción inicial es buena, pero tarde o temprano necesitaría un ejemplo de la vida real.
Robbie Dee
1
@RobbieDee: cuando el propósito del patrón es claro, su implementación también tiende a ser clara. Por ejemplo, la respuesta de Marc es absolutamente correcta, pero siento que la explicación está empantanada por la naturaleza compleja de la situación de ejemplo que usa. Esto se reduce a "Si desea construir un barco, no toque a la gente para recolectar madera y no les asigne tareas y trabajo, sino más bien enséñeles a anhelar la inmensidad infinita del mar". . En lugar de explicar qué hacer, prefiero explicar por qué hacerlo.
Flater
2
Tienes razón, por supuesto, pero no puedo evitar pensar que necesitaría un ejemplo tangible, como no tener un sistema de archivos real o una base de datos para abrir mi apetito, pero tal vez esa sea solo mi visión limitada del desarrollador :)
Robbie Dee
1

Respuesta simple a eso:

En primer lugar, una clase debe tener una responsabilidad bien definida, y todo lo que esté fuera de este alcance debe mantenerse fuera de esa clase. Dicho esto, la Inyección de dependencias es cuando se inyecta una funcionalidad de otra clase B en una clase A utilizando la ayuda de un "tercero" para lograr esta separación de preocupaciones, ayudando a la clase A a completar alguna operación que está fuera de su alcance.

.Net Core es un buen ejemplo que puede dar porque este marco utiliza mucha inyección de dependencia. En general, los servicios que desea inyectar se encuentran en el startup.csarchivo.

Claro, el alumno debe conocer algunos conceptos como el polimorfismo, las interfaces y los principios de diseño de OOP.

Fernando Calazans
fuente
0

Hay mucha pelusa y tonterías en torno a lo que es, en esencia, un concepto simple.

También es muy fácil empantanarse con " qué marco debo usar " cuando puede hacerlo simplemente en código.

Esta es la definición que uso personalmente:

El comportamiento dado X con una dependencia de Y. La inyección de dependencia implica la facilidad de suministrar cualquier Y que satisfaga los criterios para ser una instancia de Y, incluso si no tiene una.

Algunos ejemplos podrían ser donde Y es un sistema de archivos o una conexión de base de datos.

Los marcos como moq permiten que se definan dobles (versiones simuladas de Y) utilizando una interfaz para que sea posible inyectar en una instancia de Y, donde Y es, por ejemplo, una conexión de base de datos.

Es fácil caer en la trampa de creer que esto es puramente una preocupación de prueba de unidad, pero es un patrón muy útil para cualquier parte de código donde se espera un cambio y podría decirse que es una buena práctica de todos modos.

Robbie Dee
fuente
0

Proporcionamos el comportamiento de una función en tiempo de ejecución a través del método de insertar ese comportamiento en la función a través de un parámetro.

El Patrón de estrategia es un excelente ejemplo de inyección de dependencia.

ShinEmperor
fuente
0

Para hacer esto bien, primero debemos definir las dependencias y la inyección.

  • Dependencia: cualquier recurso que necesita una operación.
  • Inyección: pasar ese recurso a la operación, generalmente como un argumento para un método.

Un ejemplo rudimentario sería un método que agrega dos valores. Obviamente, este método necesita que se agreguen los valores. Si se proporcionan pasándolos como argumentos, esto ya sería un caso de inyección de dependencia. La alternativa sería implementar los operandos como propiedades o variables globales. De esa manera no se inyectarían dependencias, las dependencias estarían disponibles externamente por adelantado.

Suponga que usa propiedades en su lugar y las nombra A y B. Si cambia los nombres a Op1 y Op2, rompería el método Add. O su IDE actualizaría todos los nombres por usted, el punto es que el método también necesitaría actualizarse porque tiene dependencias de recursos externos.

Este ejemplo es básico, pero puede imaginar ejemplos más complejos donde el método realiza una operación en un objeto como una imagen o donde se lee desde una secuencia de archivos. ¿Desea que el método alcance la imagen, requiriéndole saber dónde está? No. ¿Desea que el método abra el archivo en sí mismo, requiriéndole saber dónde buscar el archivo o incluso saber que se leerá desde un archivo? No.

El punto: reducir la funcionalidad de un método a su comportamiento principal y desacoplar el método de su entorno. Obtiene el primero haciendo el segundo, puede considerar esta la definición de inyección de dependencia.

Los beneficios: dado que se han eliminado las dependencias para el entorno del método, los cambios en el método no afectarán el entorno y viceversa. => La aplicación se vuelve más fácil de mantener (modificar).

Martin Maat
fuente