Inversão de controle é um princípio de projeto genérico de arquitetura de software que auxilia na criação de estruturas de software modulares e reutilizáveis que são fáceis de manter.
É um princípio de design no qual o Fluxo de Controle é "recebido" da biblioteca genérica ou do código reutilizável.
Para entender melhor, vamos ver como costumávamos codificar em nossos primeiros dias de codificação. Em linguagens procedurais / tradicionais, a lógica de negócios geralmente controla o fluxo do aplicativo e "chama" o código / funções genéricas ou reutilizáveis. Por exemplo, em um aplicativo de console simples, meu fluxo de controle é controlado pelas instruções do meu programa, que podem incluir as chamadas para algumas funções reutilizáveis gerais.
print ("Please enter your name:");
scan (&name);
print ("Please enter your DOB:");
scan (&dob);
//More print and scan statements
<Do Something Interesting>
//Call a Library function to find the age (common code)
print Age
Em contraste, com IoC, os Frameworks são o código reutilizável que "chama" a lógica de negócios.
Por exemplo, em um sistema baseado em Windows, uma estrutura já estará disponível para criar elementos de interface do usuário como botões, menus, janelas e caixas de diálogo. Quando escrevo a lógica de negócios do meu aplicativo, são os eventos do framework que chamam meu código de lógica de negócios (quando um evento é disparado) e NÃO o contrário.
Embora o código do framework não conheça minha lógica de negócios, ele ainda saberá como chamar meu código. Isso é feito usando eventos / delegados, retornos de chamada, etc. Aqui, o controle de fluxo é "Invertido".
Portanto, em vez de depender do fluxo de controle de objetos estaticamente vinculados, o fluxo depende do gráfico geral do objeto e das relações entre os diferentes objetos.
A injeção de dependência é um padrão de design que implementa o princípio IoC para resolver dependências de objetos.
Em palavras mais simples, ao tentar escrever código, você estará criando e usando classes diferentes. Uma classe (Classe A) pode usar outras classes (Classe B e / ou D). Portanto, as classes B e D são dependências da classe A.
Uma analogia simples seria um carro da classe. Um carro pode depender de outras classes como motor, pneus e muito mais.
A injeção de dependência sugere que, em vez das classes dependentes (classe Car aqui) criarem suas dependências (classe Engine e classe Tire), a classe deve ser injetada com a instância concreta da dependência.
Vamos entender com um exemplo mais prático. Considere que você está escrevendo seu próprio TextEditor. Entre outras coisas, você pode ter um corretor ortográfico que fornece ao usuário a facilidade de verificar os erros de digitação em seu texto. Uma implementação simples de tal código pode ser:
Class TextEditor
{
//Lot of rocket science to create the Editor goes here
EnglishSpellChecker objSpellCheck;
String text;
public void TextEditor()
{
objSpellCheck = new EnglishSpellChecker();
}
public ArrayList <typos> CheckSpellings()
{
//return Typos;
}
}
À primeira vista, tudo parece rosado. O usuário escreverá algum texto. O desenvolvedor irá capturar o texto e chamar a função CheckSpellings e encontrará uma lista de erros de digitação que ele mostrará ao usuário.
Tudo parece funcionar muito bem até um belo dia, quando um usuário começa a escrever em francês no Editor.
Para fornecer suporte para mais idiomas, precisamos ter mais SpellCheckers. Provavelmente francês, alemão, espanhol etc.
Aqui, criamos um código fortemente acoplado com "English" SpellChecker estreitamente acoplado à nossa classe TextEditor, o que significa que nossa classe TextEditor é dependente de EnglishSpellChecker ou em outras palavras EnglishSpellCheker é a dependência do TextEditor. Precisamos remover essa dependência. Além disso, Nosso Editor de Texto precisa de uma maneira de manter a referência concreta de qualquer verificador ortográfico com base no critério do desenvolvedor em tempo de execução.
Portanto, como vimos na introdução do DI, ele sugere que a classe deve ser injetada com suas dependências. Portanto, deve ser responsabilidade do código de chamada injetar todas as dependências na classe / código chamado. Portanto, podemos reestruturar nosso código como
interface ISpellChecker
{
Arraylist<typos> CheckSpelling(string Text);
}
Class EnglishSpellChecker : ISpellChecker
{
public override Arraylist<typos> CheckSpelling(string Text)
{
//All Magic goes here.
}
}
Class FrenchSpellChecker : ISpellChecker
{
public override Arraylist<typos> CheckSpelling(string Text)
{
//All Magic goes here.
}
}
Em nosso exemplo, a classe TextEditor deve receber a instância concreta do tipo ISpellChecker.
Agora, a dependência pode ser injetada em um Construtor, uma Propriedade Pública ou um método.
Vamos tentar mudar nossa classe usando Constructor DI. A classe TextEditor alterada será semelhante a:
Class TextEditor
{
ISpellChecker objSpellChecker;
string Text;
public void TextEditor(ISpellChecker objSC)
{
objSpellChecker = objSC;
}
public ArrayList <typos> CheckSpellings()
{
return objSpellChecker.CheckSpelling();
}
}
Para que o código de chamada, ao criar o editor de texto, possa injetar o Tipo de SpellChecker apropriado na instância do TextEditor.
Você pode ler o artigo completo aqui