La inversión de control es un principio de diseño genérico de la arquitectura de software que ayuda a crear marcos de software modulares reutilizables que son fáciles de mantener.
Es un principio de diseño en el que el flujo de control se "recibe" de la biblioteca genérica escrita o del código reutilizable.
Para entenderlo mejor, veamos cómo solíamos codificar en nuestros primeros días de codificación. En los lenguajes de procedimiento / tradicionales, la lógica empresarial generalmente controla el flujo de la aplicación y "llama" al código / funciones genéricos o reutilizables. Por ejemplo, en una aplicación de consola simple, mi flujo de control está controlado por las instrucciones de mi programa, que pueden incluir las llamadas a algunas funciones reutilizables generales.
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
En contraste, con IoC, los Frameworks son el código reutilizable que "llama" a la lógica de negocios.
Por ejemplo, en un sistema basado en Windows, un marco ya estará disponible para crear elementos de la interfaz de usuario como botones, menús, ventanas y cuadros de diálogo. Cuando escribo la lógica de negocios de mi aplicación, serían los eventos del marco los que llamarán a mi código de lógica de negocios (cuando se dispara un evento) y NO lo contrario.
Aunque, el código del marco no tiene conocimiento de mi lógica comercial, todavía sabrá cómo llamar a mi código. Esto se logra utilizando eventos / delegados, devoluciones de llamada, etc. Aquí el control de flujo se "invierte".
Por lo tanto, en lugar de depender del flujo de control de los objetos enlazados estáticamente, el flujo depende del gráfico general del objeto y de las relaciones entre los diferentes objetos.
La inyección de dependencias es un patrón de diseño que implementa el principio de IoC para resolver dependencias de objetos.
En palabras más simples, cuando intente escribir código, creará y usará diferentes clases. Una clase (Clase A) puede usar otras clases (Clase B y / o D). Entonces, las clases B y D son dependencias de la clase A.
Una analogía simple será un auto de clase. Un automóvil puede depender de otras clases como motor, neumáticos y más.
La inyección de dependencia sugiere que, en lugar de que las clases dependientes (Class Car aquí) creen sus dependencias (Class Engine y class Tire), la clase debería ser inyectada con la instancia concreta de la dependencia.
Vamos a entender con un ejemplo más práctico. Considere que está escribiendo su propio TextEditor. Entre otras cosas, puede tener un corrector ortográfico que le brinde al usuario la posibilidad de revisar los errores tipográficos en su texto. Una implementación simple de dicho código puede 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;
}
}
A primera vista, todo parece rosado. El usuario escribirá algo de texto. El desarrollador capturará el texto y llamará a la función CheckSpellings y encontrará una lista de errores tipográficos que le mostrará al usuario.
Todo parece funcionar muy bien hasta un buen día cuando un usuario comienza a escribir francés en el Editor.
Para proporcionar soporte para más idiomas, necesitamos tener más correctores ortográficos. Probablemente francés, alemán, español, etc.
Aquí, hemos creado un código estrechamente acoplado con SpellChecker "inglés" estrechamente acoplado con nuestra clase TextEditor, lo que significa que nuestra clase TextEditor depende de EnglishSpellChecker o, en otras palabras, EnglishSpellCheker es la dependencia de TextEditor. Necesitamos eliminar esta dependencia. Además, nuestro editor de texto necesita una forma de mantener la referencia concreta de cualquier corrector ortográfico basado en el criterio del desarrollador en tiempo de ejecución.
Entonces, como vimos en la introducción de DI, sugiere que la clase debería ser inyectada con sus dependencias. Por lo tanto, debería ser responsabilidad del código de llamada inyectar todas las dependencias a la clase / código llamado. Entonces podemos reestructurar nuestro 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.
}
}
En nuestro ejemplo, la clase TextEditor debería recibir la instancia concreta del tipo ISpellChecker.
Ahora, la dependencia se puede inyectar en Constructor, una Propiedad pública o un método.
Intentemos cambiar nuestra clase usando Constructor DI. La clase TextEditor modificada se verá así:
Class TextEditor
{
ISpellChecker objSpellChecker;
string Text;
public void TextEditor(ISpellChecker objSC)
{
objSpellChecker = objSC;
}
public ArrayList <typos> CheckSpellings()
{
return objSpellChecker.CheckSpelling();
}
}
Para que el código de llamada, al crear el editor de texto, pueda inyectar el tipo de corrector ortográfico apropiado en la instancia del editor de texto.
Puedes leer el artículo completo aquí.