Pido disculpas si esto parece una repetición más de la pregunta, pero cada vez que encuentro un artículo sobre el tema, en su mayoría solo habla sobre qué es DI. Entonces, recibo DI, pero estoy tratando de entender la necesidad de un contenedor de IoC, en el que todos parecen estar metiéndose. ¿El objetivo de un contenedor de IoC es realmente "resolver automáticamente" la implementación concreta de las dependencias? Tal vez mis clases tienden a no tener varias dependencias y tal vez es por eso que no veo el gran problema, pero quiero asegurarme de que entiendo la utilidad del contenedor correctamente.
Normalmente rompo mi lógica de negocios en una clase que podría verse así:
public class SomeBusinessOperation
{
private readonly IDataRepository _repository;
public SomeBusinessOperation(IDataRespository repository = null)
{
_repository = repository ?? new ConcreteRepository();
}
public SomeType Run(SomeRequestType request)
{
// do work...
var results = _repository.GetThings(request);
return results;
}
}
Por lo tanto, solo tiene una dependencia, y en algunos casos puede tener una segunda o tercera, pero no con tanta frecuencia. Entonces, cualquier cosa que llame a esto puede pasar su propio repositorio o permitirle usar el repositorio predeterminado.
En lo que respecta a mi comprensión actual de un contenedor de IoC, todo lo que hace el contenedor es resolver IDataRepository. Pero si eso es todo lo que hace, entonces no estoy viendo un montón de valor ya que mis clases operativas ya definen un retroceso cuando no pasó ninguna dependencia. Entonces, el único otro beneficio que puedo pensar es que si tengo varias operaciones como esto usa el mismo repositorio alternativo, puedo cambiar ese repositorio en un lugar que es el registro / fábrica / contenedor. Y eso es genial, pero ¿es eso?
fuente
ConcreteRepository
y (2) puede proporcionar dependencias adicionales aConcreteRepository
(una conexión de base de datos sería común, por ejemplo).Respuestas:
El contenedor IoC no se trata del caso en el que tiene una dependencia. Se trata del caso en el que tienes 3 dependencias y ellas tienen varias dependencias que tienen dependencias, etc.
También le ayuda a centralizar la resolución de una dependencia y la gestión del ciclo de vida de las dependencias.
fuente
Hay varias razones por las que es posible que desee utilizar un contenedor de IoC.
Dlls sin referencia
Puede usar un contenedor de IoC para resolver una clase concreta a partir de un dll sin referencia. Esto significa que puede tomar dependencias completamente de la abstracción, es decir, la interfaz.
Evitar el uso de
new
Un contenedor de IoC significa que puede eliminar completamente el uso de la
new
palabra clave para crear una clase. Esto tiene dos efectos. La primera es que desacopla tus clases. El segundo (que está relacionado) es que puede colocar simulacros para pruebas unitarias. Esto es increíblemente útil, especialmente cuando está interactuando con un proceso de larga ejecución.Escribe contra las abstracciones
El uso de un contenedor de IoC para resolver sus dependencias concretas por usted le permite escribir su código contra abstracciones, en lugar de implementar cada clase concreta que necesite según lo necesite. Por ejemplo, es posible que necesite su código para leer datos de una base de datos. En lugar de escribir la clase de interacción de la base de datos, simplemente escribe una interfaz para ella y codifica contra eso. Puede usar un simulacro para probar la funcionalidad del código que está desarrollando a medida que lo desarrolla, en lugar de confiar en el desarrollo de la clase de interacción de base de datos concreta antes de poder probar el otro código.
Evitar código frágil
Otra razón para usar un contenedor de IoC es que al confiar en el contenedor de IoC para resolver sus dependencias, evita la necesidad de cambiar cada llamada a un constructor de clases cuando agrega o elimina una dependencia. El contenedor IoC resolverá automáticamente sus dependencias. Este no es un gran problema cuando creas una clase una vez, pero es un problema gigantesco cuando creas la clase en cien lugares.
Administración de por vida y limpieza de recursos no administrada
La razón final que mencionaré es la gestión de la vida útil de los objetos. Los contenedores de IoC a menudo brindan la capacidad de especificar la vida útil de un objeto. Tiene mucho sentido especificar la vida útil de un objeto en un contenedor de IoC en lugar de tratar de administrarlo manualmente en el código. La gestión manual de por vida puede ser muy difícil. Esto puede ser útil cuando se trata de objetos que requieren eliminación. En lugar de administrar manualmente la eliminación de sus objetos, algunos contenedores de IoC administrarán la eliminación por usted, lo que puede ayudar a evitar pérdidas de memoria y simplificar su base de código.
El problema con el código de muestra que ha proporcionado es que la clase que está escribiendo tiene una dependencia concreta de la clase ConcreteRepository. Un contenedor de IoC eliminaría esa dependencia.
fuente
Según el principio de responsabilidad única, cada clase debe tener una sola responsabilidad. Crear nuevas instancias de clases es solo otra responsabilidad, por lo que debe encapsular este tipo de código en una o más clases. Puede hacerlo utilizando cualquier patrón de creación, por ejemplo, fábricas, constructores, contenedores DI, etc.
Hay otros principios como la inversión de control y la inversión de dependencia. En este contexto, están relacionados con la instanciación de dependencias. Afirman que las clases de alto nivel deben estar desacopladas de las clases de bajo nivel (dependencias) que usan. Podemos desacoplar cosas creando interfaces. Por lo tanto, las clases de bajo nivel tienen que implementar interfaces específicas y las clases de alto nivel tienen que utilizar instancias de clases que implementan estas interfaces. (nota: la restricción de interfaz uniforme REST aplica el mismo enfoque a nivel de sistema).
La combinación de estos principios por ejemplo (perdón por el código de baja calidad, usé un lenguaje ad-hoc en lugar de C #, ya que no lo sé):
Sin SRP, sin IoC
Más cerca de SRP, sin IoC
Sí SRP, no IoC
Sí SRP, sí IoC
Así que terminamos teniendo un código en el que puede reemplazar cada implementación concreta por otra que implemente la misma interfaz ofc. Esto es bueno porque las clases participantes están desacopladas entre sí, solo conocen las interfaces. Otra ventaja de que el código de la instanciación es reutilizable.
fuente