O que é injeção de dependência?

3075

Já houve várias perguntas postadas com perguntas específicas sobre injeção de dependência , como quando usá-lo e quais estruturas existem para ele. Contudo,

O que é injeção de dependência e quando / por que deveria ou não deveria ser usada?

AR.
fonte
Veja minha discussão sobre injeção de dependência aqui .
Kevin S.
33
Eu concordo com os comentários sobre links. Entendo que você queira fazer referência a outra pessoa. Mas pelo menos add por que você está ligando-os e o que faz este link melhor do que os outros links eu poderia obter por meio do Google
Christian Payne
@AR: Tecnicamente, a injeção de dependência não é uma forma especial de IoC. Em vez disso, a IoC é uma técnica usada para fornecer injeção de dependência. Outras técnicas podem ser usadas para fornecer injeção de dependência (embora a IoC seja a única em uso comum), e a IoC também é usada para muitos outros problemas.
22613 Sean Reilly
Uma das explicações mais legais que eu já li sobre DI é o Guice do Google (pronunciado como suco) http://code.google.com/p/google-guice/wiki/Motivation?tm=6
Raj
136
Em relação aos links, lembre-se de que eles geralmente desaparecem de uma maneira ou de outra. Há um número crescente de links mortos nas respostas do SO. Portanto, não importa quão bom seja o artigo vinculado, ele não será bom se você não conseguir encontrá-lo.
DOK

Respostas:

1931

Injeção de Dependência está passando a dependência para outros objetos ou estrutura (injetor de dependência).

A injeção de dependência facilita o teste. A injeção pode ser feita através do construtor .

SomeClass() tem seu construtor da seguinte maneira:

public SomeClass() {
    myObject = Factory.getObject();
}

Problema : Se myObjectenvolver tarefas complexas, como acesso ao disco ou à rede, é difícil realizar testes de unidade SomeClass(). Os programadores precisam zombar myObjecte podem interceptar a chamada de fábrica.

Solução alternativa :

  • Passando myObjectcomo um argumento para o construtor
public SomeClass (MyClass myObject) {
    this.myObject = myObject;
}

myObject pode ser passado diretamente, o que facilita o teste.

  • Uma alternativa comum é definir um construtor do-nothing . A injeção de dependência pode ser feita através de setters. (h / t @MikeVella).
  • Martin Fowler documenta uma terceira alternativa (h / t @MarcDix), em que as classes implementam explicitamente uma interface para as dependências que os programadores desejam injetar.

É mais difícil isolar componentes em testes de unidade sem injeção de dependência.

Em 2013, quando escrevi esta resposta, esse era um tema importante no Blog de testes do Google . Essa continua sendo a maior vantagem para mim, pois os programadores nem sempre precisam de flexibilidade extra em seu design em tempo de execução (por exemplo, para localizador de serviço ou padrões semelhantes). Os programadores geralmente precisam isolar as classes durante o teste.

wds
fonte
25
Reconhecendo que a referência de Ben Hoffstein ao artigo de Martin Fowler é necessária para apontar uma 'leitura obrigatória' sobre o assunto, estou aceitando a resposta do wds porque ela realmente responde à pergunta aqui no SO.
AR.
121
+1 para explicação e motivação: criar objetos dos quais uma classe depende do problema de outra pessoa . Outra maneira de dizer é que o DI torna as classes mais coesas (elas têm menos responsabilidades).
Fuhrmanator
13
Você diz que a dependência é passada "para o construtor", mas, pelo que entendi, isso não é estritamente verdadeiro. Ainda é injeção de dependência se a dependência for definida como uma propriedade após o objeto ter sido instanciado, correto?
Mike Vella #
1
@MikeVella Sim, isso está correto. Na maioria dos casos, não faz diferença real, embora as propriedades geralmente sejam um pouco mais flexíveis. Vou editar um pouco o texto para apontar isso.
Wd 08/08
2
Uma das melhores respostas que encontrei até agora, estou realmente interessado em melhorá-lo. Está faltando uma descrição da terceira forma de injeção de dependência: injeção de interface .
Marc Dix
2351

A melhor definição que encontrei até agora é uma de James Shore :

"Injeção de dependência" é um termo de 25 dólares para um conceito de 5 centavos. [...] Injeção de dependência significa atribuir a um objeto suas variáveis ​​de instância. [...]

um artigo de Martin Fowler que também pode ser útil.

A injeção de dependência consiste basicamente em fornecer os objetos de que um objeto precisa (suas dependências), em vez de fazê-lo ele mesmo. É uma técnica muito útil para testes, pois permite que as dependências sejam zombadas ou eliminadas.

Dependências podem ser injetadas em objetos de várias formas (como injeção de construtor ou injeção de setter). Pode-se até usar estruturas especializadas de injeção de dependência (por exemplo, Spring) para fazer isso, mas elas certamente não são necessárias. Você não precisa dessas estruturas para ter injeção de dependência. Instanciar e passar objetos (dependências) explicitamente é uma injeção tão boa quanto a injeção por estrutura.

Thiago Arrais
fonte
35
Gosto da explicação do artigo de James, especialmente do final: "Ainda assim, você deve se maravilhar com qualquer abordagem que use três conceitos ('TripPlanner,' 'CabAgency' e 'AirlineAgency'), os transforma em mais de nove classes, e, em seguida, adiciona dezenas de linhas de código de cola e XML de configuração antes que uma única linha da lógica do aplicativo seja gravada ". Isso é o que eu tenho visto com muita frequência (infelizmente) - que a injeção de dependência (que é boa por si mesma, como explicada por ele) é mal utilizada para complicar demais as coisas que poderiam ter sido feitas com mais facilidade - acabando escrevendo código "de suporte" ...
Matt
2
Re: "Instanciar e passar objetos (dependências) explicitamente é uma injeção tão boa quanto a injeção por estrutura". Então, por que as pessoas criaram estruturas fazendo isso?
dzieciou
13
Pela mesma razão que toda estrutura recebe (ou pelo menos deveria ser) escrita: porque há muitos códigos repetidos / padrão que precisam ser escritos quando você atinge uma certa complexidade. O problema é que muitas vezes as pessoas buscam uma estrutura, mesmo quando não é estritamente necessária.
Thiago Arrais
14
O prazo de US $ 25 para um conceito de 5 centavos está morto. Aqui está um bom artigo que me ajudou: codeproject.com/Articles/615139/…
Christine
@Matt os arquivos de configuração são apenas "Adiar a decisão" levada ao extremo - "Adiar a decisão até o tempo de execução real". Dagger e, especialmente, Dagger, na minha opinião, encontraram o ponto ideal "Adiar a decisão até o momento da montagem do aplicativo".
Thorbjørn Ravn Andersen
645

Encontrei este exemplo engraçado em termos de acoplamento flexível :

Qualquer aplicativo é composto de muitos objetos que colaboram entre si para executar algumas coisas úteis. Tradicionalmente, cada objeto é responsável por obter suas próprias referências aos objetos dependentes (dependências) com os quais ele colabora. Isso leva a classes altamente acopladas e código difícil de testar.

Por exemplo, considere um Carobjeto.

A Cardepende das rodas, motor, combustível, bateria, etc. para funcionar. Tradicionalmente, definimos a marca de tais objetos dependentes, juntamente com a definição do Carobjeto.

Sem injeção de dependência (DI):

class Car{
  private Wheel wh = new NepaliRubberWheel();
  private Battery bt = new ExcideBattery();

  //The rest
}

Aqui, o Carobjeto é responsável por criar os objetos dependentes.

E se quisermos mudar o tipo de seu objeto dependente - digamos Wheel- após as NepaliRubberWheel()perfurações iniciais ? Precisamos recriar o objeto Car com sua nova dependência ChineseRubberWheel(), mas apenas o Carfabricante pode fazer isso.

Então o que isso Dependency Injectionfaz por nós ...?

Ao usar a injeção de dependência, os objetos recebem suas dependências no tempo de execução, em vez do tempo de compilação (tempo de fabricação do carro) . Para que agora possamos mudar o Wheelque quisermos. Aqui, o dependency( wheel) pode ser injetado Carno tempo de execução.

Depois de usar a injeção de dependência:

Aqui, estamos injetando as dependências (roda e bateria) em tempo de execução. Daí o termo: Injeção de Dependência.

class Car{
  private Wheel wh; // Inject an Instance of Wheel (dependency of car) at runtime
  private Battery bt; // Inject an Instance of Battery (dependency of car) at runtime
  Car(Wheel wh,Battery bt) {
      this.wh = wh;
      this.bt = bt;
  }
  //Or we can have setters
  void setWheel(Wheel wh) {
      this.wh = wh;
  }
}

Fonte: Entendendo a injeção de dependência

gtiwari333
fonte
20
Pelo que entendi, em vez de instanciar um novo objeto como parte de outro objeto, podemos injetar o referido objeto quando e se for necessário, removendo assim a dependência do primeiro objeto. Isso está certo?
JeliBeanMachine
Eu descrevi isso com um exemplo café aqui: digigene.com/design-patterns/dependency-injection-coffeeshop
Ali Nem
11
Realmente gosto dessa analogia, porque é um inglês simples, usando uma analogia simples. Diga que eu sou Toyota, já passei muito financeiramente e poder do homem em fazer um carro de design para saindo da linha de montagem, se houver produtores respeitável de pneus existentes, por que eu deveria começar do zero para fazer uma divisão fabricação de pneus ou seja, para newum pneu? Eu não. Tudo o que tenho a fazer é comprar (injetar via param) deles, instalar e wah-lah! Portanto, voltando à programação, digamos que um projeto C # precise usar uma biblioteca / classe existente, há duas maneiras de executar / depurar, com uma referência de adição a todo o projeto
#
(cont.), .. biblioteca / classe externa ou adicione 2 a partir da DLL. A menos que tenhamos que ver o que há dentro dessa classe externa, adicioná-lo como DLL é uma maneira mais fácil. Portanto, a opção 1 é a newopção 2 e a passa como parâmetro. Pode não ser preciso, mas simples e estúpido, fácil de entender.
Jeb50
1
@JeliBeanMachine (desculpe pela resposta extremamente tardia a um comentário ..) não é que removamos a dependência do primeiro objeto no objeto roda ou no objeto da bateria, é que passamos a dependência para que possamos mudar a instância ou implementação do dependência. Antes: o carro tem uma dependência codificada no NepaliRubberWheel. Depois: O carro tem uma dependência injetada na instância do Wheel.
Mikael Ohlson
263

Injeção de Dependência é uma prática em que os objetos são projetados de maneira a receber instâncias dos objetos de outras partes do código, em vez de construí-los internamente. Isso significa que qualquer objeto que implemente a interface exigida pelo objeto pode ser substituído sem alterar o código, o que simplifica o teste e melhora a dissociação.

Por exemplo, considere estas classes:

public class PersonService {
  public void addManager( Person employee, Person newManager ) { ... }
  public void removeManager( Person employee, Person oldManager ) { ... }
  public Group getGroupByManager( Person manager ) { ... }
}

public class GroupMembershipService() {
  public void addPersonToGroup( Person person, Group group ) { ... }
  public void removePersonFromGroup( Person person, Group group ) { ... }
} 

Neste exemplo, a implementação de PersonService::addManagere PersonService::removeManagerprecisaria de uma instância do GroupMembershipServicepara executar seu trabalho. Sem injeção de dependência, a maneira tradicional de fazer isso seria instanciar um novo GroupMembershipServiceno construtor PersonServicee usar esse atributo de instância em ambas as funções. No entanto, se o construtor de GroupMembershipServicetem várias coisas que exige, ou pior ainda, existem alguns "setters" de inicialização que precisam ser chamados no GroupMembershipService, o código cresce rapidamente, e o PersonServiceagora depende não apenas do GroupMembershipServicemas também de tudo o mais que GroupMembershipServicedepende de. Além disso, o vínculo GroupMembershipServiceé codificado no código, o PersonServiceque significa que você não pode "enganar" umGroupMembershipService para fins de teste ou para usar um padrão de estratégia em diferentes partes do seu aplicativo.

Com a Injeção de Dependência, em vez de instanciar o GroupMembershipServiceseu PersonService, você deve passá-lo ao PersonServiceconstrutor ou adicionar uma Propriedade (getter e setter) para definir uma instância local. Isso significa que você PersonServicenão precisa mais se preocupar em como criar um GroupMembershipService, apenas aceita os dados e trabalha com eles. Isso também significa que qualquer coisa que seja uma subclasse GroupMembershipServiceou implemente a GroupMembershipServiceinterface pode ser "injetada" no PersonServicee PersonServicenão precisa saber sobre a alteração.

Adam Ness
fonte
29
Teria sido ótimo se você pudesse dar o mesmo exemplo de código Depois de usar DI
CodyBugstein
170

A resposta aceita é boa - mas eu gostaria de acrescentar que o DI é muito parecido com o clássico de evitar constantes codificadas no código.

Quando você usa alguma constante como o nome de um banco de dados, você a move rapidamente do interior do código para algum arquivo de configuração e passa uma variável que contém esse valor para o local em que é necessário. O motivo para fazer isso é que essas constantes geralmente mudam com mais frequência que o restante do código. Por exemplo, se você deseja testar o código em um banco de dados de teste.

O DI é análogo a isso no mundo da programação orientada a objetos. Os valores lá, em vez de literais constantes, são objetos inteiros - mas o motivo para mover o código que os cria a partir do código de classe é semelhante - os objetos mudam com mais frequência do que o código que os utiliza. Um caso importante em que essa mudança é necessária são os testes.

zby
fonte
18
+1 "os objetos mudam com mais frequência do que o código que os utiliza". Para generalizar, adicione uma indireção nos pontos de fluxo. Dependendo do ponto do fluxo, os indiretos são chamados por nomes diferentes !!
Chethan 30/03
139

Vamos tentar um exemplo simples com as classes Car e Engine , qualquer carro precisa de um motor para ir a qualquer lugar, pelo menos por enquanto. Então, abaixo, como o código será exibido sem injeção de dependência.

public class Car
{
    public Car()
    {
        GasEngine engine = new GasEngine();
        engine.Start();
    }
}

public class GasEngine
{
    public void Start()
    {
        Console.WriteLine("I use gas as my fuel!");
    }
}

E para instanciar a classe Car, usaremos o próximo código:

Car car = new Car();

O problema com este código que acoplamos firmemente ao GasEngine e, se decidirmos alterá-lo para o ElectricityEngine, precisaremos reescrever a classe Car. E quanto maior o aplicativo, mais problemas e dores de cabeça teremos para adicionar e usar um novo tipo de mecanismo.

Em outras palavras, com essa abordagem, nossa classe Car de alto nível depende da classe GasEngine de nível inferior, que viola o Princípio de Inversão de Dependência (DIP) do SOLID. O DIP sugere que devemos depender de abstrações, não de classes concretas. Então, para satisfazer isso, apresentamos a interface IEngine e reescrevemos o código como abaixo:

    public interface IEngine
    {
        void Start();
    }

    public class GasEngine : IEngine
    {
        public void Start()
        {
            Console.WriteLine("I use gas as my fuel!");
        }
    }

    public class ElectricityEngine : IEngine
    {
        public void Start()
        {
            Console.WriteLine("I am electrocar");
        }
    }

    public class Car
    {
        private readonly IEngine _engine;
        public Car(IEngine engine)
        {
            _engine = engine;
        }

        public void Run()
        {
            _engine.Start();
        }
    }

Agora nossa classe Car depende apenas da interface do IEngine, não de uma implementação específica do mecanismo. Agora, o único truque é como criamos uma instância do carro e fornecemos uma classe de mecanismo concreta real, como GasEngine ou ElectricityEngine. É aí que entra a injeção de dependência .

   Car gasCar = new Car(new GasEngine());
   gasCar.Run();
   Car electroCar = new Car(new ElectricityEngine());
   electroCar.Run();

Aqui basicamente injetamos (passamos) nossa dependência (instância do mecanismo) para o construtor Car. Portanto, agora nossas classes têm acoplamentos frouxos entre objetos e suas dependências, e podemos facilmente adicionar novos tipos de motores sem alterar a classe Car.

O principal benefício da Injeção de Dependência é que as classes são mais fracamente acopladas, porque não possuem dependências codificadas. Isso segue o Princípio de Inversão de Dependência, mencionado acima. Em vez de fazer referência a implementações específicas, as classes solicitam abstrações (geralmente interfaces ) que são fornecidas quando a classe é construída.

Portanto, no final, a injeção de dependência é apenas uma técnica para obter um acoplamento flexível entre objetos e suas dependências. Em vez de instanciar diretamente dependências que a classe precisa para executar suas ações, as dependências são fornecidas à classe (na maioria das vezes) por injeção de construtor.

Além disso, quando temos muitas dependências, é uma prática muito boa usar contêineres de Inversão de Controle (IoC), que podemos dizer quais interfaces devem ser mapeadas para quais implementações concretas para todas as nossas dependências e podemos resolver essas dependências para nós quando ele constrói nosso objeto. Por exemplo, podemos especificar no mapeamento do contêiner IoC que a dependência do IEngine deve ser mapeada para a classe GasEngine e, quando solicitarmos ao contêiner IoC uma instância da nossa classe Car , ele construirá automaticamente nossa classe Car com uma dependência GasEngine Transmitido.

ATUALIZAÇÃO: assisti recentemente a um curso sobre EF Core de Julie Lerman e também gostei de sua curta definição sobre DI.

A injeção de dependência é um padrão para permitir que seu aplicativo injete objetos dinamicamente nas classes que precisam deles, sem forçar essas classes a serem responsáveis ​​por esses objetos. Ele permite que seu código seja mais livremente acoplado, e o Entity Framework Core se conecta a esse mesmo sistema de serviços.

user2771704
fonte
2
por curiosidade, como isso difere do padrão de estratégia? Esse padrão está encapsulando os algoritmos e os tornando intercambiáveis. Parece que os padrões de injeção e estratégia de dependência são muito semelhantes.
Elixir
110

Vamos imaginar que você quer pescar:

  • Sem injeção de dependência, você precisa cuidar de tudo sozinho. Você precisa encontrar um barco, comprar uma vara de pescar, procurar iscas etc. É possível, é claro, mas isso coloca muita responsabilidade em você. Em termos de software, isso significa que você precisa executar uma pesquisa para todas essas coisas.

  • Com a injeção de dependência, outra pessoa cuida de toda a preparação e disponibiliza o equipamento necessário. Você receberá ("seja injetado") o barco, a vara de pescar e a isca - tudo pronto para uso.

Olivier Liechti
fonte
59
Por outro lado, imagine que você contrate um encanador para refazer o banheiro, que então diz: "Ótimo, aqui está uma lista das ferramentas e materiais que preciso que você compre para mim". Não deveria ser o trabalho do encanador?
JSCs
Para que alguém precise cuidar de uma pessoa da qual não tem negócios, mas ainda assim decida coletar a lista de barco, vara e isca - embora esteja pronto para usar.
Chookoos
22
@ Josh Caswell Não, isso seria o trabalho do funcionário do encanador. Como cliente, você precisa de um encanamento. Para isso você precisa de um encanador. O encanador precisa de suas ferramentas. Para obtê-los, é equipado pela empresa de encanamento. Como cliente, você não quer saber exatamente o que o encanador faz ou precisa. Como encanador, você sabe o que precisa, mas só quer fazer o seu trabalho, não conseguir tudo. Como empregador de encanadores, você é responsável por equipar seus encanadores com o que eles precisam antes de enviá-los às casas das pessoas.
sara
@kai Entendo seu ponto de vista. No software, estamos falando de uma fábrica, correto? Mas o DI também significa que a classe não usa uma fábrica, pois isso ainda não é injetado. Você, o cliente, precisaria entrar em contato com o empregador (fábrica) para fornecer as ferramentas, para que você possa passar para o encanador. Não é assim que realmente funcionaria em um programa? Portanto, embora o cliente (chamando classe / função / seja o que for) não precise adquirir as ferramentas, ele ainda precisa ser o intermediário para garantir que ele chegue ao encanador (classe injetada) do empregador (fábrica).
KingOfAllTrades
1
@KingOfAllTrades: É claro que em algum momento você precisa ter alguém empregando e equipando encanadores, ou você não tem encanadores. Mas você não tem o cliente fazendo isso. O cliente apenas pede um encanador e recebe um já equipado com o que ele precisa para fazer seu trabalho. Com o DI, você ainda tem algum código para cumprir as dependências. Mas você está separando-o do código que realmente funciona. Se você levar isso ao máximo, seus objetos apenas tornarão conhecidas suas dependências, e a criação de gráficos de objetos acontecerá fora, geralmente no código init.
Chao
102

Esta é a explicação mais simples sobre Injeção de Dependência e Contêiner de Injeção de Dependência que eu já vi:

Sem injeção de dependência

  • A aplicação precisa de Foo (por exemplo, um controlador), portanto:
  • Aplicativo cria Foo
  • Aplicativo chama Foo
    • Foo precisa de Bar (por exemplo, um serviço), então:
    • Foo cria Bar
    • Foo chama Bar
      • Bar precisa do Bim (um serviço, um repositório,…), então:
      • Bar cria Bim
      • Bar faz alguma coisa

Com injeção de dependência

  • O aplicativo precisa de Foo, que precisa de Bar, que precisa de Bim, então:
  • Aplicativo cria Bim
  • Aplicativo cria Bar e fornece Bim
  • Aplicativo cria Foo e fornece barra
  • Aplicativo chama Foo
    • Foo chama Bar
      • Bar faz alguma coisa

Usando um contêiner de injeção de dependência

  • Aplicação precisa Foo assim:
  • O aplicativo obtém Foo do contêiner, portanto:
    • Container cria Bim
    • Container cria Bar e fornece Bim
    • Container cria Foo e dá Bar
  • Aplicativo chama Foo
    • Foo chama Bar
      • Bar faz alguma coisa

Injeção de dependência e recipientes de injeção de dependência são coisas diferentes:

  • Injeção de Dependência é um método para escrever código melhor
  • um Container DI é uma ferramenta para ajudar a injetar dependências

Você não precisa de um contêiner para fazer a injeção de dependência. No entanto, um contêiner pode ajudá-lo.

Pmpr
fonte
55

"Injeção de dependência" não significa apenas o uso de construtores parametrizados e configuradores públicos?

O artigo de James Shore mostra os seguintes exemplos para comparação .

Construtor sem injeção de dependência:

public class Example { 
  private DatabaseThingie myDatabase; 

  public Example() { 
    myDatabase = new DatabaseThingie(); 
  } 

  public void doStuff() { 
    ... 
    myDatabase.getData(); 
    ... 
  } 
} 

Construtor com injeção de dependência:

public class Example { 
  private DatabaseThingie myDatabase; 

  public Example(DatabaseThingie useThisDatabaseInstead) { 
    myDatabase = useThisDatabaseInstead; 
  }

  public void doStuff() { 
    ... 
    myDatabase.getData(); 
    ... 
  } 
}
JaneGoodall
fonte
Certamente na versão DI você não gostaria de inicializar o objeto myDatabase no construtor no argument? Parece não haver sentido e serviria para lançar uma exceção se você tentasse chamar DoStuff sem chamar o construtor sobrecarregado?
precisa
Somente se new DatabaseThingie()não gerar uma instância válida do myDatabase.
Jane Goodall
40

Simplificar o conceito de Injeção de Dependência. Vamos dar um exemplo de botão para alternar (ligar / desligar) uma lâmpada.

Sem injeção de dependência

O switch precisa saber com antecedência a qual bulbo estou conectado (dependência codificada). Assim,

Interruptor -> PermanentBulb // O interruptor está diretamente conectado à lâmpada permanente, não sendo possível testar facilmente

Switch(){
PermanentBulb = new Bulb();
PermanentBulb.Toggle();
}

Com injeção de dependência

Apenas a chave sabe que preciso ligar / desligar a lâmpada que for passada para mim. Assim,

Alternar -> Bulb1 OU Bulb2 OU NightBulb (dependência injetada)

Switch(AnyBulb){ //pass it whichever bulb you like
AnyBulb.Toggle();
}

Modificando James Example para Switch e Bulb:

public class SwitchTest { 
  TestToggleBulb() { 
    MockBulb mockbulb = new MockBulb(); 

    // MockBulb is a subclass of Bulb, so we can 
    // "inject" it here: 
    Switch switch = new Switch(mockBulb); 

    switch.ToggleBulb(); 
    mockBulb.AssertToggleWasCalled(); 
  } 
}

public class Switch { 
  private Bulb myBulb; 

  public Switch() { 
    myBulb = new Bulb(); 
  } 

  public Switch(Bulb useThisBulbInstead) { 
    myBulb = useThisBulbInstead; 
  } 

  public void ToggleBulb() { 
    ... 
    myBulb.Toggle(); 
    ... 
  } 
}`
wakqasahmed
fonte
36

O que é injeção de dependência (DI)?

Como já foi dito, o Injection de Dependência (DI) remove a responsabilidade da criação direta e do gerenciamento da vida útil de outras instâncias de objetos das quais nossa classe de interesse (classe de consumidor) depende (no sentido UML ). Em vez disso, essas instâncias são passadas para nossa classe de consumidor, normalmente como parâmetros do construtor ou por meio de configuradores de propriedades (o gerenciamento do objeto de dependência que instancia e passa para a classe de consumidor geralmente é realizado por uma Inversão de controle (IoC) contêiner , mas esse é outro tópico) .

DI, DIP e SOLID

Especificamente, no paradigma dos princípios SOLID de Robert C Martin de Design Orientado a Objetos , DIé uma das possíveis implementações do Princípio de Inversão de Dependências (DIP) . O DIP é o Ddo SOLIDmantra - outras implementações DIP incluem o Locator Service, e os padrões de Plugin.

O objetivo do DIP é dissociar dependências estreitas e concretas entre classes e, em vez disso, afrouxar o acoplamento por meio de uma abstração, que pode ser alcançada por meio de interface, abstract classoupure virtual class , dependendo da linguagem e abordagem utilizada.

Sem o DIP, nosso código (eu chamei essa 'classe consumidora') está diretamente acoplado a uma dependência concreta e também costuma ser sobrecarregado com a responsabilidade de saber como obter e gerenciar uma instância dessa dependência, ou seja, conceitualmente:

"I need to create/use a Foo and invoke method `GetBar()`"

Considerando que, após a aplicação do DIP, o requisito é diminuído e a preocupação em obter e gerenciar a vida útil do Foo dependência foi removida:

"I need to invoke something which offers `GetBar()`"

Por que usar DIP (e DI)?

A dissociação de dependências entre classes dessa maneira permite fácil substituição dessas classes de dependência por outras implementações que também atendem aos pré-requisitos da abstração (por exemplo, a dependência pode ser alternada com outra implementação da mesma interface). Além disso, como outros já mencionaram, possivelmente o motivo mais comum para desacoplar classes por meio do DIP é permitir que uma classe consumidora seja testada isoladamente, pois essas mesmas dependências agora podem ser stubadas e / ou zombadas.

Uma conseqüência do DI é que o gerenciamento da vida útil das instâncias do objeto de dependência não é mais controlado por uma classe consumidora, pois o objeto de dependência agora é passado para a classe consumidora (via injeção de construtor ou setter).

Isso pode ser visto de diferentes maneiras:

  • Se o controle da vida útil das dependências pela classe consumidora precisar ser mantido, o controle poderá ser restabelecido injetando uma fábrica (abstrata) para criar as instâncias da classe de dependência na classe consumidora. O consumidor poderá obter instâncias por meio de umCreate na fábrica, conforme necessário, e descartá-las uma vez concluídas.
  • Ou, o controle da vida útil das instâncias de dependência pode ser abandonado em um contêiner de IoC (mais sobre isso abaixo).

Quando usar DI?

  • Onde provavelmente houver a necessidade de substituir uma dependência por uma implementação equivalente,
  • Sempre que você precisar testar os métodos de uma classe isoladamente de suas dependências,
  • Onde a incerteza da vida útil de uma dependência pode justificar a experimentação (por exemplo, ei, o MyDepClassthread é seguro - e se o tornarmos um singleton e injetarmos a mesma instância em todos os consumidores?)

Exemplo

Aqui está uma implementação simples de C #. Dada a classe Consumidor abaixo:

public class MyLogger
{
   public void LogRecord(string somethingToLog)
   {
      Console.WriteLine("{0:HH:mm:ss} - {1}", DateTime.Now, somethingToLog);
   }
}

Embora aparentemente inócuo, ele tem duas staticdependências de duas outras classes System.DateTimeeSystem.Console , que não apenas limitam as opções de saída de log (o log no console será inútil se ninguém estiver assistindo), mas, pior ainda, é difícil testar automaticamente, dada a dependência de um relógio do sistema não determinístico.

No entanto, podemos aplicar DIPa essa classe, abstraindo a preocupação do registro de data e hora como uma dependência e acoplando MyLoggerapenas a uma interface simples:

public interface IClock
{
    DateTime Now { get; }
}

Também podemos diminuir a dependência Consolede uma abstração, como a TextWriter. A injeção de dependência é normalmente implementada como constructorinjeção (passando uma abstração para uma dependência como parâmetro para o construtor de uma classe consumidora) ou Setter Injection(passando a dependência por meio de um setXyz()setter ou de uma propriedade .Net {set;}definida). A injeção de construtor é preferida, pois isso garante que a classe estará em um estado correto após a construção e permite que os campos de dependência internos sejam marcados como readonly(C #) ou final(Java). Portanto, usando a injeção de construtor no exemplo acima, isso nos deixa com:

public class MyLogger : ILogger // Others will depend on our logger.
{
    private readonly TextWriter _output;
    private readonly IClock _clock;

    // Dependencies are injected through the constructor
    public MyLogger(TextWriter stream, IClock clock)
    {
        _output = stream;
        _clock = clock;
    }

    public void LogRecord(string somethingToLog)
    {
        // We can now use our dependencies through the abstraction 
        // and without knowledge of the lifespans of the dependencies
        _output.Write("{0:yyyy-MM-dd HH:mm:ss} - {1}", _clock.Now, somethingToLog);
    }
}

Clocknecessário fornecer um concreto que, naturalmente, poderia reverter paraDateTime.Now e as duas dependências precisam ser fornecidas por um contêiner de IoC via injeção de construtor)

Um teste de unidade automatizado pode ser construído, o que prova definitivamente que nosso criador de logs está funcionando corretamente, pois agora temos controle sobre as dependências - o tempo e podemos espionar a saída escrita:

[Test]
public void LoggingMustRecordAllInformationAndStampTheTime()
{
    // Arrange
    var mockClock = new Mock<IClock>();
    mockClock.Setup(c => c.Now).Returns(new DateTime(2015, 4, 11, 12, 31, 45));
    var fakeConsole = new StringWriter();

    // Act
    new MyLogger(fakeConsole, mockClock.Object)
        .LogRecord("Foo");

    // Assert
    Assert.AreEqual("2015-04-11 12:31:45 - Foo", fakeConsole.ToString());
}

Próximos passos

A injeção de dependência está invariavelmente associada a um contêiner de Inversão de Controle (IoC) , para injetar (fornecer) as instâncias de dependência concretas e gerenciar instâncias de vida útil. Durante o processo de configuração / inicialização, os IoCcontêineres permitem que seja definido o seguinte:

  • mapeamento entre cada abstração e a implementação concreta configurada (por exemplo, "sempre que um consumidor solicitar IBare retornar uma ConcreteBarinstância" )
  • as políticas podem ser configuradas para o gerenciamento da vida útil de cada dependência, por exemplo, para criar um novo objeto para cada instância do consumidor, para compartilhar uma instância de dependência singleton entre todos os consumidores, para compartilhar a mesma instância de dependência apenas no mesmo encadeamento, etc.
  • No .Net, os contêineres de IoC conhecem protocolos como IDisposablee assumem a responsabilidade de Disposingdependências, de acordo com o gerenciamento da vida útil configurada.

Normalmente, depois que os contêineres IoC são configurados / inicializados, eles operam perfeitamente em segundo plano, permitindo que o codificador se concentre no código em questão, em vez de se preocupar com dependências.

A chave do código compatível com DI é evitar o acoplamento estático de classes e não usar new () para a criação de dependências

Como no exemplo acima, a dissociação de dependências exige algum esforço de design e, para o desenvolvedor, é necessária uma mudança de paradigma para quebrar o hábito de newdependências diretamente e, em vez disso, confiar no contêiner para gerenciar dependências.

Mas os benefícios são muitos, especialmente na capacidade de testar minuciosamente sua classe de interesse.

Nota : A criação / mapeamento / projeção (via new ..()) de projeções POCO / POJO / DTOs de serialização / gráficos de entidades / JSON anônimas e outros - ou seja, classes ou registros "Somente dados" - usados ​​ou retornados de métodos não são considerados dependências Sentido UML) e não sujeito a DI. Usar newpara projetar isso é ótimo.

StuartLC
fonte
1
O problema é DIP! = DI. O DIP trata da dissociação da abstração da implementação: A. Os módulos de alto nível não devem depender dos módulos de baixo nível. Ambos devem depender de abstrações. B. As abstrações não devem depender de detalhes. Os detalhes devem depender de abstrações. A DI é uma maneira de dissociar a criação de objetos do uso de objetos.
Ricardo Rivaldo
Sim, a distinção está claramente declarada no meu parágrafo 2, "DI uma das possíveis implementações do DIP" , no paradigma SOLID do tio Bob. Também deixei isso claro em um post anterior.
StuartLC 16/03/19
25

O objetivo principal da injeção de dependência (DI) é manter o código-fonte do aplicativo limpo e estável :

  • limpeza do código de inicialização da dependência
  • estável, independentemente da dependência usada

Praticamente, todo padrão de design separa as preocupações para que mudanças futuras afetem os arquivos mínimos.

O domínio específico da DI é a delegação de configuração e inicialização de dependência.

Exemplo: DI com script de shell

Se você ocasionalmente trabalha fora de Java, lembre-se de como sourceé frequentemente usado em muitas linguagens de script (Shell, Tcl etc.) ou mesmo importem Python que são mal utilizadas para esse fim).

Considere um dependent.shscript simples :

#!/bin/sh
# Dependent
touch         "one.txt" "two.txt"
archive_files "one.txt" "two.txt"

O script é dependente: não será executado com êxito por conta própria (archive_files não está definido).

Você define archive_filesno archive_files_zip.shscript de implementação (usando zipneste caso):

#!/bin/sh
# Dependency
function archive_files {
    zip files.zip "$@"
}

Em vez de source-ing script de implementação diretamente no dependente, você usa um injector.sh"contêiner" que agrupa os dois "componentes":

#!/bin/sh 
# Injector
source ./archive_files_zip.sh
source ./dependent.sh

A archive_files dependência acaba de ser injetada no script dependente .

Você poderia ter injetado dependência que implementa archive_filesusando taror xz.

Exemplo: removendo DI

Se o dependent.shscript usasse dependências diretamente, a abordagem seria chamada de pesquisa de dependência (que é oposta à injeção de dependência ):

#!/bin/sh
# Dependent

# dependency look-up
source ./archive_files_zip.sh

touch         "one.txt" "two.txt"
archive_files "one.txt" "two.txt"

Agora, o problema é que o "componente" dependente precisa executar a própria inicialização.

O código fonte do "componente" não é limpo nem estável pois todas as alterações na inicialização de dependências requerem nova liberação para o arquivo de código-fonte do "componente".

Últimas palavras

O DI não é tão amplamente enfatizado e popularizado quanto nas estruturas Java.

Mas é uma abordagem genérica para dividir as preocupações de:

  • desenvolvimento de aplicativos ( ciclo de vida de liberação do código fonte único )
  • implantação de aplicativos ( vários ambientes de destino com ciclos de vida independentes)

O uso da configuração apenas com pesquisa de dependência não ajuda, pois o número de parâmetros de configuração pode mudar por dependência (por exemplo, novo tipo de autenticação), bem como o número de tipos de dependências suportados (por exemplo, novo tipo de banco de dados).

uvsmtid
fonte
Eu acrescentaria a capacidade de concluir uma classe específica (teste) sem precisar completar suas dependências, como um objetivo para o DI.
David
22

Todas as respostas acima são boas, meu objetivo é explicar o conceito de uma maneira simples, para que qualquer pessoa sem conhecimento de programação também possa entender o conceito.

A injeção de dependência é um dos padrões de design que nos ajudam a criar sistemas complexos de maneira mais simples.

Podemos ver uma ampla variedade de aplicações desse padrão em nossa vida cotidiana. Alguns exemplos são gravador, VCD, unidade de CD etc.

Gravador de fita portátil de bobina a bobina, meados do século XX.

A imagem acima é uma imagem do gravador de rolo portátil, meados do século XX. Fonte .

A principal intenção de uma máquina gravadora é gravar ou reproduzir som.

Ao projetar um sistema, é necessário um rolo para gravar ou reproduzir som ou música. Existem duas possibilidades para projetar este sistema

  1. podemos colocar o carretel dentro da máquina
  2. podemos fornecer um gancho para o carretel onde ele pode ser colocado.

Se usarmos o primeiro, precisamos abrir a máquina para trocar o rolo. se optarmos pelo segundo, que está colocando um gancho para o rolo, obteremos um benefício adicional de tocar qualquer música alterando o rolo. e também reduzindo a função apenas para reproduzir o que quer que esteja no rolo.

Como injeção de dependência sábia, é o processo de externalizar as dependências para focar apenas na funcionalidade específica do componente, de modo que componentes independentes possam ser acoplados para formar um sistema complexo.

Os principais benefícios que alcançamos usando a injeção de dependência.

  • Alta coesão e acoplamento solto.
  • Externalizando a dependência e olhando apenas para a responsabilidade.
  • Fazendo as coisas como componentes e combinar para formar um grande sistema com alta capacidade.
  • Ajuda a desenvolver componentes de alta qualidade, pois eles são desenvolvidos de forma independente e são testados adequadamente.
  • Ajuda a substituir o componente por outro, se um falhar.

Hoje em dia, esse conceito forma a base de estruturas bem conhecidas no mundo da programação. O Spring Angular etc são as estruturas de software conhecidas construídas no topo desse conceito

Injeção de dependência é um padrão usado para criar instâncias de objetos nos quais outros objetos confiam sem saber em tempo de compilação qual classe será usada para fornecer essa funcionalidade ou simplesmente a maneira de injetar propriedades em um objeto é chamada de injeção de dependência.

Exemplo para injeção de dependência

Anteriormente, estamos escrevendo código como este

Public MyClass{
 DependentClass dependentObject
 /*
  At somewhere in our code we need to instantiate 
  the object with new operator  inorder to use it or perform some method.
  */ 
  dependentObject= new DependentClass();
  dependentObject.someMethod();
}

Com a injeção de dependência, o injetor de dependência decola a instanciação para nós

Public MyClass{
 /* Dependency injector will instantiate object*/
 DependentClass dependentObject

 /*
  At somewhere in our code we perform some method. 
  The process of  instantiation will be handled by the dependency injector
 */ 

  dependentObject.someMethod();
}

Você também pode ler

Diferença entre Inversão de Controle e Injeção de Dependência

samuelj90
fonte
17

O que é injeção de dependência?

Injeção de Dependência (DI) significa dissociar os objetos que são dependentes um do outro. Digamos que o objeto A seja dependente do Objeto B, portanto a idéia é separar esses objetos um do outro. Não precisamos codificar o objeto usando uma nova palavra-chave, em vez de compartilhar dependências para objetos em tempo de execução, apesar do tempo de compilação. Se falamos sobre

Como a injeção de dependência funciona na primavera:

Não precisamos codificar o objeto usando a nova palavra-chave, em vez de definir a dependência do bean no arquivo de configuração. O contêiner de mola será responsável por conectar todos.

Inversão de Controle (COI)

O COI é um conceito geral e pode ser expresso de diversas maneiras e a Injeção de Dependência é um exemplo concreto do COI.

Dois tipos de injeção de dependência:

  1. Injeção de Construtor
  2. Injeção de Setter

1. Injeção de dependência baseada em construtor:

O DI baseado em construtor é realizado quando o contêiner chama um construtor de classe com vários argumentos, cada um representando uma dependência de outra classe.

public class Triangle {

private String type;

public String getType(){
    return type;
 }

public Triangle(String type){   //constructor injection
    this.type=type;
 }
}
<bean id=triangle" class ="com.test.dependencyInjection.Triangle">
        <constructor-arg value="20"/>
  </bean>

2. Injeção de dependência baseada em setter:

O DI baseado em setter é realizado pelo contêiner que chama métodos setter em seus beans após a invocação de um construtor sem argumento ou método de fábrica estático sem argumento para instanciar seu bean.

public class Triangle{

 private String type;

 public String getType(){
    return type;
  }
 public void setType(String type){          //setter injection
    this.type = type;
  }
 }

<!-- setter injection -->
 <bean id="triangle" class="com.test.dependencyInjection.Triangle">
        <property name="type" value="equivialteral"/>

NOTA: É uma boa regra geral usar argumentos de construtor para dependências obrigatórias e setters para dependências opcionais. Observe que a anotação se usarmos anotação baseada em @Required em um setter pode ser usada para fazer setters como dependências necessárias.

Harleen
fonte
15

A melhor analogia em que consigo pensar é no cirurgião e seus assistentes em uma sala de operações, onde o cirurgião é a pessoa principal e seu assistente que fornece os vários componentes cirúrgicos quando ele precisa para que o cirurgião possa se concentrar naquele coisa que ele faz melhor (cirurgia). Sem o assistente, o cirurgião precisa obter os componentes sempre que precisar.

DI, para abreviar, é uma técnica para remover uma responsabilidade adicional comum (carga) dos componentes para buscar os componentes dependentes, fornecendo-os a eles.

O DI aproxima você do princípio de responsabilidade única (SR), como o surgeon who can concentrate on surgery.

Quando usar o DI: eu recomendaria usá-lo em quase todos os projetos de produção (pequenos / grandes), especialmente em ambientes de negócios sempre em mudança :)

Motivo: como você deseja que seu código seja facilmente testável, zombável, etc., para que você possa testar rapidamente suas alterações e enviá-lo ao mercado. Além disso, por que você não faria isso quando houvesse muitas ferramentas / estruturas gratuitas impressionantes para ajudá-lo em sua jornada para uma base de código onde você tem mais controle.

Anwar Husain
fonte
@WindRider Obrigado. Eu não posso concordar mais. A vida e o corpo humano são exemplos magníficos de excelência em design ... a coluna vertebral é um excelente exemplo de um ESB:) ...
Anwar Husain
15

Exemplo, temos 2 classes Cliente Service. ClientusaráService

public class Service {
    public void doSomeThingInService() {
        // ...
    }
}

Sem injeção de dependência

Caminho 1)

public class Client {
    public void doSomeThingInClient() {
        Service service = new Service();
        service.doSomeThingInService();
    }
}

Caminho 2)

public class Client {
    Service service = new Service();
    public void doSomeThingInClient() {
        service.doSomeThingInService();
    }
}

Caminho 3)

public class Client {
    Service service;
    public Client() {
        service = new Service();
    }
    public void doSomeThingInClient() {
        service.doSomeThingInService();
    }
}

1) 2) 3) Usando

Client client = new Client();
client.doSomeThingInService();

Vantagens

  • Simples

Desvantagens

  • Difícil para teste Client classe de
  • Quando mudamos o Serviceconstrutor, precisamos alterar o código em todos os lugaresService objeto

Usar injeção de dependência

Maneira 1) Injeção de construtor

public class Client {
    Service service;

    Client(Service service) {
        this.service = service;
    }

    // Example Client has 2 dependency 
    // Client(Service service, IDatabas database) {
    //    this.service = service;
    //    this.database = database;
    // }

    public void doSomeThingInClient() {
        service.doSomeThingInService();
    }
}

Usando

Client client = new Client(new Service());
// Client client = new Client(new Service(), new SqliteDatabase());
client.doSomeThingInClient();

Maneira 2) Injeção do setter

public class Client {
    Service service;

    public void setService(Service service) {
        this.service = service;
    }

    public void doSomeThingInClient() {
        service.doSomeThingInService();
    }
}

Usando

Client client = new Client();
client.setService(new Service());
client.doSomeThingInClient();

Caminho 3) Injeção de interface

Verifica https://en.wikipedia.org/wiki/Dependency_injection

===

Agora, esse código já está sendo seguido Dependency Injectione é mais fácil para a Clientclasse de teste .
No entanto, ainda usamos new Service()muito tempo e não é bom quando o Serviceconstrutor de alterações . Para evitá-lo, podemos usar o injetor DI como
1) Manual simplesInjector

public class Injector {
    public static Service provideService(){
        return new Service();
    }

    public static IDatabase provideDatatBase(){
        return new SqliteDatabase();
    }
    public static ObjectA provideObjectA(){
        return new ObjectA(provideService(...));
    }
}

Usando

Service service = Injector.provideService();

2) Usar biblioteca: para Android dagger2

Vantagens

  • Facilitar o teste
  • Quando você altera o Service, você só precisa alterá-lo na classe Injector
  • Se você usar use Constructor Injection, quando olhar para o construtor de Client, verá quantas dependências de Clientclasse

Desvantagens

  • Se você usar use Constructor Injection, o Serviceobjeto será criado quando Clientcriado. Às vezes, usamos a função na Clientclasse sem o uso, Serviceportanto, o Servicedesperdício é desperdiçado.

Definição de Injeção de Dependência

https://en.wikipedia.org/wiki/Dependency_injection

Uma dependência é um objeto que pode ser usado ( Service)
Uma injeção é a passagem de uma dependência ( Service) para um objeto dependente ( Client) que a usaria

Phan Van Linh
fonte
13

Isso significa que os objetos devem ter apenas quantas dependências forem necessárias para realizar seu trabalho e as dependências devem ser poucas. Além disso, as dependências de um objeto devem estar em interfaces e não em objetos "concretos", quando possível. (Um objeto concreto é qualquer objeto criado com a palavra-chave new.) O acoplamento flexível promove maior reutilização, manutenção mais fácil e permite que você forneça facilmente objetos "simulados" em vez de serviços caros.

A "Injeção de Dependência" (DI) também é conhecida como "Inversão de Controle" (IoC), pode ser usada como uma técnica para incentivar esse acoplamento solto.

Existem duas abordagens principais para implementar a DI:

  1. Injeção de construtor
  2. Injeção de incubadora

Injeção de construtor

É a técnica de passar dependências de objetos para seu construtor.

Observe que o construtor aceita uma interface e não um objeto concreto. Além disso, observe que uma exceção será lançada se o parâmetro orderDao for nulo. Isso enfatiza a importância de receber uma dependência válida. Injeção de construtor é, na minha opinião, o mecanismo preferido para atribuir a um objeto suas dependências. É claro para o desenvolvedor, enquanto invoca o objeto, quais dependências precisam ser dadas ao objeto "Pessoa" para uma execução adequada.

Injeção de Setter

Mas considere o exemplo a seguir ... Suponha que você tenha uma classe com dez métodos que não têm dependências, mas você está adicionando um novo método que depende do IDAO. Você pode alterar o construtor para usar a Injeção de construtor, mas isso pode forçá-lo a alterar todas as chamadas de construtor em todo o lugar. Como alternativa, você pode simplesmente adicionar um novo construtor que aceite a dependência, mas como um desenvolvedor sabe facilmente quando usar um construtor sobre o outro. Por fim, se a dependência é muito cara de criar, por que deveria ser criada e transmitida ao construtor quando só pode ser usada raramente? “Injeção de incubadora” é outra técnica de DI que pode ser usada em situações como essa.

A injeção do setter não força dependências a serem passadas para o construtor. Em vez disso, as dependências são definidas nas propriedades públicas expostas pelo objeto em necessidade. Como implicado anteriormente, os principais motivadores para fazer isso incluem:

  1. Suporte à injeção de dependência sem precisar modificar o construtor de uma classe herdada.
  2. Permitindo que recursos ou serviços caros sejam criados o mais tarde possível e somente quando necessário.

Aqui está o exemplo de como seria o código acima:

public class Person {
    public Person() {}

    public IDAO Address {
        set { addressdao = value; }
        get {
            if (addressdao == null)
              throw new MemberAccessException("addressdao" +
                             " has not been initialized");
            return addressdao;
        }
    }

    public Address GetAddress() {
       // ... code that uses the addressdao object
       // to fetch address details from the datasource ...
    }

    // Should not be called directly;
    // use the public property instead
    private IDAO addressdao;
Piyush Deshpande
fonte
3
Acho que seu primeiro parágrafo se afasta da questão e não é de todo a definição de DI (ou seja, você está tentando definir SOLID, não DI). Tecnicamente, mesmo se você tiver 100 dependências, ainda poderá usar a injeção de dependência. Da mesma forma, é possível injetar dependências concretas - ainda é a injeção de dependências.
Jay Sullivan
10

Eu acho que desde que todos tenham escrito para DI, deixe-me fazer algumas perguntas ..

  1. Quando você tem uma configuração de DI em que todas as implementações reais (não interfaces) que serão injetadas em uma classe (por exemplo, serviços para um controlador), por que isso não é algum tipo de código fixo?
  2. E se eu quiser alterar o objeto em tempo de execução? Por exemplo, minha configuração já diz que quando eu instanciar o MyController, injeto o FileLogger como ILogger. Mas eu posso querer injetar DatabaseLogger.
  3. Toda vez que quero alterar quais objetos meu AClass precisa, agora preciso procurar em dois lugares - a própria classe e o arquivo de configuração. Como isso facilita a vida?
  4. Se a Aproperty of AClass não for injetada, é mais difícil zombar dele?
  5. Voltando à primeira pergunta. Se o uso de um novo objeto () é ruim, como injetar a implementação e não a interface? Acho que muitos de vocês estão dizendo que estamos realmente injetando a interface, mas a configuração faz com que você especifique a implementação dessa interface. Não em tempo de execução. Ela é codificada durante o tempo de compilação.

Isso se baseia na resposta que @Adam N postou.

Por que o PersonService não precisa mais se preocupar com o GroupMembershipService? Você acabou de mencionar que GroupMembership possui várias coisas (objetos / propriedades) das quais depende. Se o GMService fosse necessário no PService, você o teria como propriedade. Você pode zombar disso, independentemente de você ter injetado ou não. A única vez que eu gostaria que fosse injetado seria se o GMService tivesse classes filho mais específicas, que você não saberia até o tempo de execução. Então você deseja injetar a subclasse. Ou se você quiser usá-lo como singleton ou protótipo. Para ser sincero, o arquivo de configuração tem tudo codificado até a subclasse de um tipo (interface) que ele injeta durante o tempo de compilação.

EDITAR

Um bom comentário de Jose Maria Arranz em DI

O DI aumenta a coesão ao remover qualquer necessidade de determinar a direção da dependência e escrever qualquer código de cola.

Falso. A direção das dependências está no formato XML ou como anotações, suas dependências são gravadas como código XML e anotações. XML e anotações SÃO código fonte.

O DI reduz o acoplamento, tornando todos os seus componentes modulares (ou seja, substituíveis) e tendo interfaces bem definidas entre si.

Falso. Você não precisa de uma estrutura DI para criar um código modular baseado em interfaces.

Sobre substituíveis: com um arquivo .properties muito simples e Class.forName, você pode definir quais classes podem mudar. Se QUALQUER classe do seu código puder ser alterada, Java não é para você, use uma linguagem de script. A propósito: as anotações não podem ser alteradas sem a recompilação.

Na minha opinião, há uma única razão para as estruturas DI: redução da placa da caldeira. Com um sistema de fábrica bem-feito, você pode fazer o mesmo, mais controlado e mais previsível que sua estrutura DI preferida, as estruturas DI prometem redução de código (XML e anotações também são código fonte). O problema é que a redução da placa da caldeira é real apenas em casos muito simples (uma instância por classe e semelhante), às vezes no mundo real escolher o objeto de serviço apropriado não é tão fácil quanto mapear uma classe para um objeto singleton.

Chookoos
fonte
8

As respostas populares são inúteis, porque definem a injeção de dependência de uma maneira que não é útil. Vamos concordar que por "dependência" queremos dizer algum outro objeto preexistente que nosso objeto X precisa. Mas não dizemos que estamos fazendo "injeção de dependência" quando dizemos

$foo = Foo->new($bar);

Apenas chamamos esses parâmetros de passagem para o construtor. Fazemos isso regularmente desde que os construtores foram inventados.

"Injeção de dependência" é considerado um tipo de "inversão de controle", o que significa que alguma lógica é retirada do chamador. Esse não é o caso quando o chamador passa os parâmetros, portanto, se isso fosse DI, DI não implicaria inversão de controle.

DI significa que existe um nível intermediário entre o responsável pela chamada e o construtor que gerencia dependências. Um Makefile é um exemplo simples de injeção de dependência. O "chamador" é a pessoa que digita "make bar" na linha de comando e o "construtor" é o compilador. O Makefile especifica que a barra depende de foo e faz uma

gcc -c foo.cpp; gcc -c bar.cpp

antes de fazer um

gcc foo.o bar.o -o bar

A pessoa digitando "make bar" não precisa saber que a barra depende de foo. A dependência foi injetada entre "make bar" e gcc.

O principal objetivo do nível intermediário não é apenas passar as dependências para o construtor, mas listar todas as dependências em apenas um local e ocultá-las do codificador (não fazer com que o codificador as forneça).

Geralmente, o nível intermediário fornece fábricas para os objetos construídos, que devem fornecer uma função que cada tipo de objeto solicitado deve satisfazer. Isso porque, ao ter um nível intermediário que oculta os detalhes da construção, você já incorreu na penalidade de abstração imposta pelas fábricas, para que você também possa usá-las.

Phil Goetz
fonte
8

Injeção de Dependência significa uma maneira (na verdade de qualquer maneira ) de uma parte do código (por exemplo, uma classe) ter acesso a dependências (outras partes do código, por exemplo, outras classes, depende) de uma maneira modular sem que elas sejam codificadas (portanto eles podem ser alterados ou substituídos livremente ou até carregados em outro momento, conforme necessário)

(e ps, sim, tornou-se um nome exagerado em 25 $ para um conceito bastante simples) , meus .25centavos

Nikos M.
fonte
8

Sei que já existem muitas respostas, mas achei muito útil: http://tutorials.jenkov.com/dependency-injection/index.html

Sem dependência:

public class MyDao {

  protected DataSource dataSource = new DataSourceImpl(
    "driver", "url", "user", "password");

  //data access methods...
  public Person readPerson(int primaryKey) {...}     
}

Dependência:

public class MyDao {

  protected DataSource dataSource = null;

  public MyDao(String driver, String url, String user, String password) {
    this.dataSource = new DataSourceImpl(driver, url, user, password);
  }

  //data access methods...
  public Person readPerson(int primaryKey) {...}
}

Observe como a DataSourceImplinstanciação é movida para um construtor. O construtor usa quatro parâmetros, que são os quatro valores necessários para o DataSourceImpl. Embora a MyDaoclasse ainda dependa desses quatro valores, ela não satisfaz mais essas dependências. Eles são fornecidos por qualquer classe que esteja criando uma MyDaoinstância.

Ali Issa
fonte
1
Você não passaria pela interface que o DataSourceImp já havia construído?
PmanAce
6

A injeção de dependência é uma solução possível para o que geralmente poderia ser chamado de requisito de "Ofuscação da dependência". Ocultação de dependência é um método de tirar a natureza "óbvia" do processo de fornecer uma dependência a uma classe que a exige e, portanto, ofuscar, de alguma forma, o fornecimento da referida dependência à referida classe. Isto não é necessariamente uma coisa ruim. De fato, ofuscando a maneira pela qual uma dependência é fornecida a uma classe, algo fora da classe é responsável por criar a dependência, o que significa que, em vários cenários, uma implementação diferente da dependência pode ser fornecida à classe sem fazer alterações. para a aula. Isso é ótimo para alternar entre os modos de produção e teste (por exemplo, usando uma dependência de serviço 'falsa').

Infelizmente, a parte ruim é que algumas pessoas assumiram que você precisa de uma estrutura especializada para ofuscar a dependência e que você é de algum modo um programador 'menor' se optar por não usar uma estrutura específica para isso. Outro mito extremamente perturbador, acreditado por muitos, é que a injeção de dependência é a única maneira de obter a ofuscação da dependência. Isso é demonstrável, histórica e obviamente 100% errado, mas você terá problemas para convencer algumas pessoas de que existem alternativas à injeção de dependência para seus requisitos de ofuscação de dependência.

Os programadores entendem o requisito de ofuscação de dependência há anos e muitas soluções alternativas evoluíram antes e depois da concepção da injeção de dependência. Existem padrões de fábrica, mas também existem muitas opções usando o ThreadLocal onde nenhuma injeção é necessária em uma instância específica - a dependência é efetivamente injetada no encadeamento, o que tem o benefício de disponibilizar o objeto (por meio de métodos getter estáticos convenientes). qualquerclasse que exige isso sem ter que adicionar anotações às classes que exigem isso e configurar 'cola' XML complexa para que isso aconteça. Quando suas dependências são necessárias para persistência (JPA / JDO ou o que for), permite que você obtenha 'persistência tranparente' muito mais fácil e com classes de modelo de domínio e modelo de negócio compostas apenas por POJOs (ou seja, nenhuma estrutura específica / bloqueada em anotações).

Volksman
fonte
5

Antes de ir para a descrição técnica, visualize-a primeiro com um exemplo da vida real, porque você encontrará muitas coisas técnicas para aprender a injeção de dependência, mas o tempo máximo que pessoas como eu não conseguem entender o conceito principal.

Na primeira foto, suponha que você tenha uma fábrica de automóveis com muitas unidades. Um carro é realmente construído na unidade de montagem, mas precisa de motor , assentos e rodas . Portanto , a unidade de montagem depende dessas unidades e são dependências da fábrica.

Você pode sentir que agora é muito complicado manter todas as tarefas nesta fábrica porque, junto com a tarefa principal (Montar carro na unidade de montagem), você também precisa se concentrar em outras unidades . Agora, é muito caro manter e o prédio da fábrica é enorme, por isso é preciso muito dinheiro para alugar.

Agora, olhe para a segunda foto. Se você encontrar algumas empresas fornecedoras que fornecerão a você roda , assento e motor mais baratos que o custo de autoprodução, agora você não precisa instalá-las em sua fábrica. Agora você pode alugar um prédio menor apenas para sua unidade de montagem, o que diminuirá sua tarefa de manutenção e reduzirá o custo extra de aluguel. Agora você também pode se concentrar apenas na sua tarefa principal (montagem do carro).

Agora podemos dizer que todas as dependências para montar um carro são injetadas na fábrica pelos fornecedores . É um exemplo de injeção de dependência (DI) da vida real .

Agora, na palavra técnica, injeção de dependência é uma técnica pela qual um objeto (ou método estático) fornece as dependências de outro objeto. Portanto, transferir a tarefa de criar o objeto para outra pessoa e usar diretamente a dependência é chamado de injeção de dependência.

Isso irá ajudá-lo agora a aprender DI com alguma palavra técnica. Isso mostrará quando usar DI e quando não deve .

Tudo em uma fábrica de automóveis.

Fábrica de carros simples

Gk Mohammad Emon
fonte
1
resposta mais clara dos 40 ish. Exemplo e imagens da vida real. +1. Deve ser a resposta aceita.
Marche Remi
4

do Book Apress.Spring.Persistence.with.Hibernate.Oct.2010

O objetivo da injeção de dependência é dissociar o trabalho de resolver componentes de software externos da lógica de negócios do aplicativo. Sem injeção de dependência, os detalhes de como um componente acessa os serviços necessários podem ficar confusos com o código do componente. Isso não apenas aumenta o potencial de erros, adiciona inchaço ao código e amplia as complexidades de manutenção; ele une os componentes mais estreitamente, dificultando a modificação de dependências ao refatorar ou testar.

BERGUIGA Mohamed Amine
fonte
4

A Injeção de Dependência (DI) é uma das Design Patterns, que usa o recurso básico do OOP - o relacionamento em um objeto com outro objeto. Enquanto a herança herda um objeto para tornar mais complexo e específico outro objeto, o relacionamento ou a associação simplesmente cria um ponteiro para outro objeto de um objeto usando o atributo O poder do DI está em combinação com outros recursos do OOP, assim como as interfaces e o código oculto. Suponha que tenhamos um cliente (assinante) na biblioteca, que pode emprestar apenas um livro por simplicidade.

Interface do livro:

package com.deepam.hidden;

public interface BookInterface {

public BookInterface setHeight(int height);
public BookInterface setPages(int pages);   
public int getHeight();
public int getPages();  

public String toString();
}

Em seguida, podemos ter muitos tipos de livros; um dos tipos é ficção:

package com.deepam.hidden;

public class FictionBook implements BookInterface {
int height = 0; // height in cm
int pages = 0; // number of pages

/** constructor */
public FictionBook() {
    // TODO Auto-generated constructor stub
}

@Override
public FictionBook setHeight(int height) {
  this.height = height;
  return this;
}

@Override
public FictionBook setPages(int pages) {
  this.pages = pages;
  return this;      
}

@Override
public int getHeight() {
    // TODO Auto-generated method stub
    return height;
}

@Override
public int getPages() {
    // TODO Auto-generated method stub
    return pages;
}

@Override
public String toString(){
    return ("height: " + height + ", " + "pages: " + pages);
}
}

Agora, o assinante pode ter associação ao livro:

package com.deepam.hidden;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Subscriber {
BookInterface book;

/** constructor*/
public Subscriber() {
    // TODO Auto-generated constructor stub
}

// injection I
public void setBook(BookInterface book) {
    this.book = book;
}

// injection II
public BookInterface setBook(String bookName) {
    try {
        Class<?> cl = Class.forName(bookName);
        Constructor<?> constructor = cl.getConstructor(); // use it for parameters in constructor
        BookInterface book = (BookInterface) constructor.newInstance();
        //book = (BookInterface) Class.forName(bookName).newInstance();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (SecurityException e) {
        e.printStackTrace();
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
    return book;
}

public BookInterface getBook() {
  return book;
}

public static void main(String[] args) {

}

}

Todas as três classes podem ser ocultadas para sua própria implementação. Agora podemos usar este código para DI:

package com.deepam.implement;

import com.deepam.hidden.Subscriber;
import com.deepam.hidden.FictionBook;

public class CallHiddenImplBook {

public CallHiddenImplBook() {
    // TODO Auto-generated constructor stub
}

public void doIt() {
    Subscriber ab = new Subscriber();

    // injection I
    FictionBook bookI = new FictionBook();
    bookI.setHeight(30); // cm
    bookI.setPages(250);
    ab.setBook(bookI); // inject
    System.out.println("injection I " + ab.getBook().toString());

    // injection II
    FictionBook bookII = ((FictionBook) ab.setBook("com.deepam.hidden.FictionBook")).setHeight(5).setPages(108); // inject and set
    System.out.println("injection II " + ab.getBook().toString());      
}

public static void main(String[] args) {
    CallHiddenImplBook kh = new CallHiddenImplBook();
    kh.doIt();
}
}

Existem várias maneiras diferentes de usar a injeção de dependência. É possível combiná-lo com Singleton, etc., mas ainda é básico, apenas a associação é realizada através da criação de atributo do tipo de objeto dentro de outro objeto. A utilidade é única e somente característica, esse código, que devemos escrever repetidamente, está sempre preparado e pronto para seguir adiante. É por isso que o DI está tão intimamente ligado à Inversion of Control (IoC), o que significa que nosso programa passa a controlar outro módulo em execução, que injeta beans no nosso código. (Cada objeto que pode ser injetado pode ser assinado ou considerado como um Bean.) Por exemplo, no Spring, isso é feito através da criação e inicialização ApplicationContextcontainer, o que funciona para nós. Simplesmente em nosso código, criamos o Contexto e chamamos a inicialização dos beans. Nesse momento, a injeção foi feita automaticamente.

hariprasad
fonte
4

Injeção de Dependência para crianças de 5 anos.

Quando você tira as coisas da geladeira, pode causar problemas. Você pode deixar a porta aberta, pode conseguir algo que mamãe ou papai não querem que você tenha. Você pode até estar procurando por algo que nem sequer temos ou que expirou.

O que você deve fazer é declarar uma necessidade: "Preciso beber alguma coisa no almoço" e, em seguida, garantiremos que você tenha alguma coisa quando se sentar para comer.

Nithin Prasad
fonte
1
Esta é claramente a resposta dos pais. ;)
Marche Remi
4

De Christoffer Noring, o livro de Pablo Deeleman "Learning Angular - Second Edition":

"À medida que nossos aplicativos crescem e evoluem, cada uma de nossas entidades de código exigirá internamente instâncias de outros objetos , mais conhecidos como dependências no mundo da engenharia de software. A ação de passar essas dependências para o cliente dependente é conhecida como injeção , e implica também a participação de outra entidade de código, chamado de injector . o injector irá assumir a responsabilidade por instanciar e bootstrapping o necessário dependênciasentão eles estão prontos para uso a partir do momento em que são injetados com sucesso no cliente. Isso é muito importante, pois o cliente não sabe nada sobre como instanciar suas próprias dependências e só conhece a interface. que implementa para usá-las. "

De: Anton Moiseev. livro "Desenvolvimento Angular com Texto Datilografado, Segunda Edição".

"Em resumo, o DI ajuda a escrever o código de uma maneira pouco acoplada e torna seu código mais testável e reutilizável ."

HS Progr
fonte
3

Em palavras simples, a injeção de dependência (DI) é a maneira de remover dependências ou acoplamentos estreitos entre objetos diferentes. A injeção de dependência oferece um comportamento coeso para cada objeto.

DI é a implementação do diretor do COI da Spring que diz "Não ligue para nós, ligaremos para você". O uso do programador de injeção de dependência não precisa criar objetos usando a nova palavra-chave.

Os objetos são carregados uma vez no contêiner Spring e, em seguida, os reutilizamos sempre que precisamos, buscando esses objetos no contêiner Spring usando o método getBean (String beanName).

Waqas Ahmed
fonte
3

A injeção de dependência é o cerne do conceito relacionado ao Spring Framework. Ao criar o quadro de qualquer projeto, a primavera pode desempenhar um papel vital, e aqui a injeção de dependência entra no jarro.

Na verdade, suponha que em java você tenha criado duas classes diferentes como classe A e classe B, e qualquer que seja a função disponível na classe B que você deseja usar na classe A, portanto, nesse momento, a injeção de dependência pode ser usada. onde você pode criar objetos de uma classe em outra, da mesma maneira que você pode injetar uma classe inteira em outra classe para torná-lo acessível. dessa maneira, a dependência pode ser superada.

A INJEÇÃO POR DEPENDÊNCIA SIMPLESMENTE ESTÁ COLOCANDO DUAS AULAS E AO MESMO TEMPO MANTENDO-AS SEPARADAS.

mohit sarsar
fonte