Isso é realmente simples de fazer quando você entende que o DI é sobre padrões e princípios , não sobre tecnologia.
Para projetar a API de maneira independente de container DI, siga estes princípios gerais:
Programe para uma interface, não uma implementação
Esse princípio é, na verdade, uma citação (da memória) dos Design Patterns , mas sempre deve ser seu objetivo real . O DI é apenas um meio de atingir esse fim .
Aplique o Princípio de Hollywood
O Princípio de Hollywood em termos de DI diz: Não ligue para o DI Container, ele ligará para você .
Nunca solicite diretamente uma dependência chamando um contêiner de dentro do seu código. Peça implicitamente usando Injeção de Construtor .
Usar injeção de construtor
Quando você precisar de uma dependência, peça-a estaticamente através do construtor:
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 como a classe Service garante seus invariantes. Depois que uma instância é criada, a dependência é garantida para estar disponível devido à combinação da Cláusula Guard e da readonly
palavra - chave.
Use Abstract Factory se precisar de um objeto de vida curta
As dependências injetadas com a Injeção de construtor tendem a ter vida útil prolongada, mas às vezes você precisa de um objeto de vida útil curta ou construir a dependência com base em um valor conhecido apenas no tempo de execução.
Veja isso para mais informações.
Componha apenas no último momento responsável
Mantenha os objetos dissociados até o fim. Normalmente, você pode esperar e conectar tudo no ponto de entrada do aplicativo. Isso é chamado de raiz da composição .
Mais detalhes aqui:
Simplifique usando uma fachada
Se você acha que a API resultante se torna muito complexa para usuários iniciantes, sempre pode fornecer algumas classes de Fachada que encapsulam combinações de dependências comuns.
Para fornecer uma Fachada flexível com um alto grau de descoberta, considere fornecer o Fluent Builders. Algo assim:
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);
}
}
Isso permitiria ao usuário criar um Foo padrão escrevendo
var foo = new MyFacade().CreateFoo();
No entanto, seria muito possível descobrir que é possível fornecer uma dependência personalizada e você pode escrever
var foo = new MyFacade().WithDependency(new CustomDependency()).CreateFoo();
Se você imagina que a classe MyFacade encapsula muitas dependências diferentes, espero que fique claro como ela forneceria os padrões adequados e, ao mesmo tempo, tornaria a extensibilidade detectável.
FWIW, muito tempo depois de escrever esta resposta, ampliei os conceitos aqui contidos e escrevi uma postagem mais longa no blog sobre DI-Friendly Libraries , e uma publicação complementar sobre DI-Friendly Frameworks .