Esto es realmente simple de hacer una vez que entiendes que la DI se trata de patrones y principios , no de tecnología.
Para diseñar la API de forma independiente del contenedor DI, siga estos principios generales:
Programa para una interfaz, no una implementación
Este principio es en realidad una cita (de memoria) de Patrones de diseño , pero siempre debe ser su objetivo real . DI es solo un medio para lograr ese fin .
Aplicar el principio de Hollywood
El Principio de Hollywood en términos de DI dice: No llame al contenedor DI, lo llamará a usted .
Nunca solicite directamente una dependencia llamando a un contenedor desde su código. Solicítelo implícitamente usando la inyección de constructor .
Usar inyección de constructor
Cuando necesite una dependencia, solicítela estáticamente a través del constructor:
public class Service : IService
{
private readonly ISomeDependency dep;
public Service(ISomeDependency dep)
{
if (dep == null)
{
throw new ArgumentNullException("dep");
}
this.dep = dep;
}
public ISomeDependency Dependency
{
get { return this.dep; }
}
}
Observe cómo la clase de Servicio garantiza sus invariantes. Una vez que se crea una instancia, se garantiza que la dependencia estará disponible debido a la combinación de la cláusula de protección y la readonly
palabra clave.
Use Abstract Factory si necesita un objeto de vida corta
Las dependencias inyectadas con la inyección de constructor tienden a ser de larga duración, pero a veces se necesita un objeto de corta duración, o para construir la dependencia en función de un valor conocido solo en tiempo de ejecución.
Vea esto para más información.
Componer solo en el último momento responsable
Mantenga los objetos desacoplados hasta el final. Normalmente, puede esperar y conectar todo en el punto de entrada de la aplicación. Esto se llama la raíz de composición .
Más detalles aquí:
Simplifica usando una Fachada
Si cree que la API resultante se vuelve demasiado compleja para usuarios novatos, siempre puede proporcionar algunas clases de Fachada que encapsulan combinaciones de dependencia comunes.
Para proporcionar una fachada flexible con un alto grado de visibilidad, podría considerar proporcionar constructores con fluidez. Algo como esto:
public class MyFacade
{
private IMyDependency dep;
public MyFacade()
{
this.dep = new DefaultDependency();
}
public MyFacade WithDependency(IMyDependency dependency)
{
this.dep = dependency;
return this;
}
public Foo CreateFoo()
{
return new Foo(this.dep);
}
}
Esto permitiría a un usuario crear un Foo predeterminado escribiendo
var foo = new MyFacade().CreateFoo();
Sin embargo, sería muy reconocible que sea posible proporcionar una dependencia personalizada, y podría escribir
var foo = new MyFacade().WithDependency(new CustomDependency()).CreateFoo();
Si imagina que la clase MyFacade encapsula muchas dependencias diferentes, espero que quede claro cómo proporcionaría los valores predeterminados adecuados y al mismo tiempo haría que la extensibilidad sea reconocible.
FWIW, mucho después de escribir esta respuesta, amplié los conceptos en este documento y escribí una publicación de blog más larga sobre Bibliotecas DI-Friendly , y una publicación complementaria sobre Frameworks DI-Friendly .