Inversão de controle é um princípio genérico de design da arquitetura de software que auxilia na criação de estruturas de software modulares e reutilizáveis, fáceis de manter.
É um princípio de design no qual o fluxo de controle é "recebido" da biblioteca escrita genérica ou do código reutilizável.
Para entender melhor, vamos ver como costumávamos codificar em nossos dias anteriores de codificação. Em linguagens processuais / tradicionais, a lógica comercial geralmente controla o fluxo do aplicativo e "chama" o código / funções genéricas ou reutilizáveis. Por exemplo, em um aplicativo simples de console, 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 a 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 janelas, uma estrutura já estará disponível para criar elementos da interface do usuário, como botões, menus, janelas e caixas de diálogo. Quando eu escrevo a lógica comercial do meu aplicativo, seriam os eventos da estrutura que chamarão o código da lógica comercial (quando um evento for disparado) e NÃO o contrário.
Embora o código da estrutura não esteja ciente da minha lógica de negócios, ele ainda saberá como chamar meu código. Isso é alcançado usando eventos / delegados, retornos de chamada etc. Aqui, o controle de fluxo é "invertido".
Portanto, em vez de depender do fluxo de controle em objetos vinculados estaticamente, o fluxo depende do gráfico geral dos objetos e das relações entre diferentes objetos.
Injeção de Dependência é um padrão de design que implementa o princípio de IoC para resolver dependências de objetos.
Em palavras mais simples, quando você estiver tentando escrever código, estará criando e usando diferentes classes. 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 será um carro de classe. Um carro pode depender de outras classes, como motor, pneus e muito mais.
A injeção de dependência sugere que, em vez de as classes dependentes (carro da classe aqui) criarem suas dependências (mecanismo de classe e classe pneu), 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 desse 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 em que um usuário começa a escrever 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 associado ao SpellChecker "inglês", fortemente associado à nossa classe TextEditor, o que significa que nossa classe TextEditor depende do EnglishSpellChecker ou, por 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 corretor ortográfico com base na discrição do desenvolvedor em tempo de execução.
Portanto, como vimos na introdução do DI, isso 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. Para que possamos 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.
}
}
No nosso exemplo, a classe TextEditor deve receber a instância concreta do tipo ISpellChecker.
Agora, a dependência pode ser injetada no Construtor, em uma Propriedade Pública ou em um método.
Vamos tentar mudar nossa classe usando o 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 SpellChecker apropriado na instância do Editor de Texto.
Você pode ler o artigo completo aqui