Qual é a melhor definição para injeção de dependência?


10

Toda vez que alguém me chega e me pede para definir a Injeção de Dependência de uma maneira conceitual e explicar os prós e contras reais do uso de DI no design de software. Confesso que tenho algumas dificuldades para explicar os conceitos de DI. Toda vez que preciso contar a eles a história sobre o princípio de responsabilidade única, composição sobre herança etc.

Alguém pode me ajudar a explicar a melhor maneira de descrever o DI para desenvolvedores?


2
O desafio aqui é que existem muitas definições conflitantes de DI. Eu adoto a postura "DI puro": se eu tiver uma função que depende de seus parâmetros para fornecer todo o estado, dados, etc., essa função está usando o DI. No outro extremo, alguns argumentam que sem uma estrutura de DI, não há injeção de dependência (embora eles estejam errados, é claro;)). Então, se você pode pregar para baixo uma definição, você não pode começar a explicar o que é ...
David Arno

Então, pelo que entendi, este não é apenas um problema meu.
Tiago Sampaio



Tudo se resume a isso: injeção de dependência é uma técnica usada para obter inversão de dependência; tudo o resto é apenas material extra construído sobre isso. Observe que nesses dois termos a palavra "dependência" tem significados ligeiramente diferentes. Na injeção de dependência, refere-se ao componente do qual o código depende. Na inversão de dependência, refere-se ao próprio relacionamento (direcionado) - aquele que queremos inverter. O último é o objetivo; portanto, os principais prós e contras são os mesmos; além de algumas preocupações extras relacionadas à implementação real, como o gerenciamento da vida útil do objeto.
Filip Milovanović

Respostas:


22

Injeção de Dependência é um nome horrível (IMO) 1 para um conceito bastante direto. Aqui está um exemplo:

  1. Você tem um método (ou classe com métodos) que executa o X (por exemplo, recuperar dados do banco de dados)
  2. Como parte da execução do X, o referido método cria e gerencia um recurso interno (por exemplo, a DbContext). Esse recurso interno é chamado de dependência
  3. Você remove a criação e o gerenciamento do recurso (ou seja DbContext) do método e torna responsabilidade do chamador fornecer esse recurso (como parâmetro do método ou por instanciação da classe)
  4. Agora você está fazendo injeção de dependência.


[1] : Eu sou de nível inferior e levei meses para me sentar e aprender a injeção de dependência, porque o nome indica que seria algo muito mais complicado, como DLL Injection . O fato de o Visual Studio (e nós desenvolvedores em geral) se referir às bibliotecas .NET (DLLs ou assemblies ) dos quais um projeto depende, pois as dependências não ajuda em nada. Existe até o Walker de Dependências (depends.exe) .


[Editar] Imaginei que algum código de demonstração seria útil para alguns, então aqui está um (em C #).

Sem injeção de dependência:

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

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

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

Seu consumidor faria algo como:

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

A mesma classe implementada com padrão de injeção de dependência seria assim:

public class RepositoryWithDI
{
    protected DbContext Context { get; }

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

Agora é responsabilidade do chamador instanciar DbContexte passar (errar, injetar ) para sua classe:

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

    // work
}

3
Isso deve ser adicionado à Wikipedia.
Evorlor 21/02/19

2
Agora é responsabilidade do chamador instanciar um DbContext - acho que seria responsabilidade do ponto de entrada do aplicativo instanciar todas as dependências necessárias. Portanto, o consumidor precisa apenas introduzir o tipo necessário como uma dependência no próprio contrato.
Fabio

@ Fabio Poderia ser. (Nesse caso, a responsabilidade do chamador forneceria o recurso que foi instanciado na inicialização do aplicativo ao método / classe que está sendo chamado.) No meu exemplo, porém, não é, porque não é um requisito para explicar o conceito de injeção de dependência .
Marc.2377 22/09/19

5

Os conceitos abstratos são frequentemente melhor explicados usando uma analogia do mundo real. Esta é a minha analogia básica:

Você administra uma lanchonete. Você faz sanduíches incríveis, mas sabe pouco ou nada sobre o pão em si. Você só tem pão branco sem graça. Seu trabalho se concentra inteiramente nas coberturas usadas para transformar o pão em um sanduíche.

No entanto, alguns de seus clientes realmente preferem pão integral. Alguns preferem cereais integrais. Você realmente não se importa, você pode fazer qualquer sanduíche incrível, desde que seja um pão de tamanho semelhante. Você também não quer assumir a responsabilidade adicional de adquirir vários tipos de pão e manter os estoques. Mesmo se você estocar vários tipos de pão, sempre haverá algum cliente com um sabor exótico no pão que você não poderia prever razoavelmente.

Então você institui uma nova regra: os clientes trazem seu próprio pão. Você já não fornece pão. Essa é uma situação em que todos saem ganhando: os clientes conseguem o pão exato que desejam e você não precisa mais se preocupar em comprar o pão que não gosta. Afinal, você é uma sanduicheira, não um padeiro.

Ah, e para acomodar os clientes que não querem comprar seu próprio pão, você abre uma segunda loja ao lado que vende seus pães brancos sem graça originais. Os clientes que não trouxeram seu próprio pão simplesmente precisam obter o padrão e depois vêm até você para fazer um sanduíche.

Não é perfeito, mas destaca o principal recurso: dar controle ao consumidor . O benefício mútuo inerente é que você não precisa mais adquirir suas próprias dependências, e seu consumidor é impedido na escolha da dependência.


1
Eu gosto disso, mas o OP está procurando uma explicação para os desenvolvedores . Uma abstração inicial é boa, mas mais cedo ou mais tarde, seria necessário um exemplo da vida real.
Robbie Dee

1
@RobbieDee: Quando o objetivo do padrão é claro, sua implementação tende a ficar clara também. Por exemplo, a resposta de Marc está absolutamente correta, mas sinto que a explicação está ficando atolada pela natureza complexa da situação exemplar que ele usa. Isso se resume a "Se você deseja construir um navio, não instale pessoas para coletar madeira e não lhes atribua tarefas e trabalho, mas ensine-as a almejar a imensidão infinita do mar". . Em vez de explicar o que fazer, prefiro explicar por que fazê-lo.
Flater

2
Você está certo, é claro, mas não posso deixar de pensar que precisaria de um exemplo tangível - como não ter um sistema de arquivos ou banco de dados real para aguçar meu apetite, mas talvez essa seja apenas minha visão restrita do desenvolvedor :)
Robbie Dee

1

Resposta simples para isso:

Antes de tudo, uma classe deve ter uma responsabilidade bem definida e tudo fora desse escopo deve ser mantido fora dessa classe. Dito isso, Injeção de Dependência é quando você injeta uma funcionalidade de outra classe B em uma classe A usando a ajuda de "terceiros" para obter essa separação de preocupações, ajudando a classe A a concluir alguma operação fora do seu escopo.

O .Net Core é um bom exemplo que você pode dar, porque essa estrutura usa muita injeção de dependência. Geralmente, os serviços que você deseja injetar estão localizados no startup.csarquivo.

Certamente, o aluno deve estar ciente de alguns conceitos como polimorfismo, interfaces e princípios de design de OOP.


0

Há muito fluff e bunkum em torno do que é, em essência, um conceito simples.

Também é muito fácil se atolar com " qual estrutura devo usar " quando você pode fazer isso simplesmente no código.

Esta é a definição que eu pessoalmente uso:

Dado o comportamento X com uma dependência de Y. A injeção de dependência envolve a facilidade de fornecer qualquer Y que satisfaça os critérios de ser uma instância de Y, mesmo se você não tiver uma.

Alguns exemplos podem ser onde Y é um sistema de arquivos ou uma conexão com o banco de dados.

Estruturas como moq permitem que duplas (versões falsas de Y) sejam definidas usando uma interface, para que seja possível injetar em uma instância de Y, onde Y é, por exemplo, uma conexão com o banco de dados.

É fácil cair na armadilha de acreditar que isso é puramente uma preocupação de teste de unidade, mas é um padrão muito útil para qualquer parte do código em que a mudança é esperada e, sem dúvida, é uma boa prática.


0

Fornecemos o comportamento de uma função em tempo de execução através do método de inserir esse comportamento na função por meio de um parâmetro

O Padrão de Estratégia é um excelente exemplo de injeção de dependência.


0

Para fazer isso direito, precisamos primeiro definir dependências e injeção.

  • Dependência: qualquer recurso que uma operação precise.
  • Injeção: passando esse recurso para a operação, normalmente como argumento para um método.

Um exemplo rudimentar seria um método que adiciona dois valores. Obviamente, esse método precisa que os valores sejam adicionados. Se eles forem fornecidos passando-os como argumentos, isso já seria um caso de injeção de dependência. A alternativa seria implementar os operandos como propriedades ou variáveis ​​globais. Dessa forma, nenhuma dependência seria injetada, as dependências estariam disponíveis externamente antecipadamente.

Suponha que você use propriedades e nomeie-as como A e B. Se você alterar os nomes para Op1 e Op2, você poderá interromper o método Add. Ou seu IDE atualizaria todos os nomes para você, o ponto é que o método precisaria ser atualizado também porque possui dependências de recursos externos.

Este exemplo é básico, mas você pode imaginar exemplos mais complexos em que o método executa uma operação em um objeto como uma imagem ou onde ele lê de um fluxo de arquivos. Deseja que o método alcance a imagem, exigindo que ela saiba onde está? Não. Deseja que o método abra o próprio arquivo, exigindo que ele saiba onde procurar o arquivo ou até mesmo que saiba que ele estará lendo um arquivo? Não.

O ponto: reduzir a funcionalidade de um método ao seu comportamento principal e separar o método do ambiente. Você obtém o primeiro fazendo o segundo; você pode considerar isso a definição de injeção de dependência.

Os benefícios: como as dependências para o ambiente do método foram eliminadas, as alterações no método não afetarão o ambiente e vice-versa. => O aplicativo fica mais fácil de manter (modificar).

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.