Peço desculpas se isso parece mais uma repetição da pergunta, mas toda vez que encontro um artigo sobre o assunto, ele fala apenas sobre o que é DI. Então, eu tenho DI, mas estou tentando entender a necessidade de um contêiner de IoC, no qual todo mundo parece estar se metendo. O objetivo de um contêiner de IoC é realmente apenas "resolver automaticamente" a implementação concreta das dependências? Talvez minhas aulas tendam a não ter várias dependências e talvez seja por isso que não vejo grande coisa, mas quero ter certeza de que estou entendendo a utilidade do contêiner corretamente.
Normalmente, divido minha lógica de negócios em uma classe que pode ser algo como isto:
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;
}
}
Portanto, ele possui apenas uma dependência e, em alguns casos, pode ter uma segunda ou terceira, mas não com tanta frequência. Portanto, qualquer coisa que chama isso pode passar por seu próprio repositório ou permitir que ele use o repositório padrão.
No que diz respeito ao meu conhecimento atual de um contêiner de IoC, tudo o que faz é resolver o IDataRepository. Mas se isso é tudo o que faz, não estou vendo muito valor, pois minhas classes operacionais já definem um fallback quando nenhuma dependência passou. Portanto, o único outro benefício em que consigo pensar é que se eu tiver várias operações como Se você usar o mesmo repositório de fallback, posso alterá-lo em um local que seja o registro / fábrica / contêiner. E isso é ótimo, mas é isso?
fonte
ConcreteRepository
e (2) você pode fornecer dependências adicionaisConcreteRepository
(uma conexão com o banco de dados seria comum, por exemplo).Respostas:
O contêiner de IoC não é sobre o caso em que você tem uma dependência. É sobre o caso em que você tem 3 dependências e elas têm várias dependências que têm dependências etc.
Também ajuda a centralizar a resolução de uma dependência e o gerenciamento do ciclo de vida de dependências.
fonte
Existem várias razões pelas quais você pode querer usar um contêiner de IoC.
DLLs não referenciadas
Você pode usar um contêiner de IoC para resolver uma classe concreta de uma dll não referenciada. Isso significa que você pode assumir dependências inteiramente da abstração - ou seja, da interface.
Evite o uso de
new
Um contêiner de IoC significa que você pode remover completamente o uso da
new
palavra - chave para criar uma classe. Isso tem dois efeitos. A primeira é que desacopla suas aulas. O segundo (que está relacionado) é que você pode soltar zombarias para testes de unidade. Isso é incrivelmente útil, principalmente quando você está interagindo com um processo de execução longa.Escrever contra abstrações
O uso de um contêiner de IoC para resolver suas dependências concretas permite que você escreva seu código em abstrações, em vez de implementar todas as classes concretas necessárias conforme necessário. Por exemplo, você pode precisar do seu código para ler dados de um banco de dados. Em vez de escrever a classe de interação do banco de dados, você simplesmente escreve uma interface para ela e codifica contra ela. Você pode usar uma simulação para testar a funcionalidade do código que está desenvolvendo enquanto o desenvolve, em vez de confiar no desenvolvimento da classe de interação concreta do banco de dados antes de poder testar o outro código.
Evite código quebradiço
Outro motivo para usar um contêiner de IoC é que, contando com o contêiner de IoC para resolver suas dependências, você evita a necessidade de alterar todas as chamadas para um construtor de classe ao adicionar ou remover uma dependência. O contêiner de IoC resolverá automaticamente suas dependências. Esse não é um problema enorme quando você cria uma classe uma vez, mas é um problema gigantesco quando você cria a classe em centenas de lugares.
Gerenciamento vitalício e limpeza de recursos não gerenciados
A última razão que mencionarei é o gerenciamento da vida útil do objeto. Os contêineres de IoC geralmente fornecem a capacidade de especificar a vida útil de um objeto. Faz muito sentido especificar a vida útil de um objeto em um contêiner de IoC, em vez de tentar gerenciá-lo manualmente no código. O gerenciamento manual da vida útil pode ser muito difícil. Isso pode ser útil ao lidar com objetos que requerem descarte. Em vez de gerenciar manualmente o descarte de seus objetos, alguns contêineres IoC gerenciarão o descarte para você, o que pode ajudar a evitar vazamentos de memória e simplificar sua base de código.
O problema com o código de exemplo que você forneceu é que a classe que você está escrevendo tem uma dependência concreta da classe ConcreteRepository. Um contêiner de IoC removeria essa dependência.
fonte
De acordo com o princípio da responsabilidade única, toda classe deve ter apenas uma responsabilidade. Criar novas instâncias de classes é apenas outra responsabilidade, portanto você deve encapsular esse tipo de código em uma ou mais classes. Você pode fazer isso usando qualquer padrão de criação, por exemplo, fábricas, construtores, contêineres DI etc ...
Existem outros princípios como inversão de controle e inversão de dependência. Nesse contexto, eles estão relacionados à instanciação de dependências. Eles afirmam que as classes de alto nível devem ser dissociadas das classes de baixo nível (dependências) usadas. Podemos dissociar as coisas criando interfaces. Portanto, as classes de baixo nível precisam implementar interfaces específicas e as classes de alto nível precisam utilizar instâncias de classes que implementam essas interfaces. (nota: a restrição de interface uniforme REST aplica a mesma abordagem no nível do sistema.)
A combinação desses princípios por exemplo (desculpe pelo código de baixa qualidade, usei uma linguagem ad-hoc em vez de C #, pois não sei disso):
Sem SRP, sem IoC
Mais próximo do SRP, sem IoC
Sim SRP, não IoC
Sim SRP, sim IoC
Então, acabamos tendo um código no qual você pode substituir todas as implementações concretas por outras que implementam a mesma interface ofc. Portanto, isso é bom porque as aulas participantes são separadas, elas conhecem apenas as interfaces. Outra vantagem é que o código da instanciação é reutilizável.
fonte