JavaRush /Blog Java /Random-ES /Una manera fácil de inyectar dependencias.

Una manera fácil de inyectar dependencias.

Publicado en el grupo Random-ES
La inyección de dependencia (DI) no es un concepto fácil de entender y aplicarlo a aplicaciones nuevas o existentes es aún más confuso. Jess Smith le muestra cómo realizar una inyección de dependencia sin un contenedor de inyección en los lenguajes de programación C# y Java. Forma sencilla de inyección de dependencia - 1En este artículo, le mostraré cómo implementar la inyección de dependencia (DI) en aplicaciones .NET y Java. El concepto de inyección de dependencia llamó la atención de los desarrolladores por primera vez en 2000, cuando Robert Martin escribió el artículo "Principios y patrones de diseño" (más tarde conocido por el acrónimo SOLID ). La D en SOLID se refiere a la Dependencia de Inversión (DOI), que más tarde se conoció como inyección de dependencia. La definición original y más común: la inversión de dependencias es una inversión de la forma en que una clase base gestiona las dependencias. El artículo original de Martin utilizó el siguiente código para ilustrar la dependencia de una clase Copyde una clase de nivel inferior WritePrinter:
void Copy()
	{
	 int c;
	 while ((c = ReadKeyboard()) != EOF)
		WritePrinter(c);
	}
El primer problema obvio es que si cambia la lista de parámetros o los tipos de un método WritePrinter, necesita implementar actualizaciones siempre que exista una dependencia de ese método. Este proceso aumenta los costos de mantenimiento y es una fuente potencial de nuevos errores.
¿Interesado en leer sobre Java? ¡ Únase al grupo de desarrolladores de Java !
Otro problema: la clase Copiar ya no es un candidato potencial para su reutilización. Por ejemplo, ¿qué sucede si necesita enviar los caracteres ingresados ​​desde el teclado a un archivo en lugar de a una impresora? Para hacer esto, puede modificar la clase Copyde la siguiente manera (sintaxis del lenguaje C++):
void Copy(outputDevice dev)
	{
	int c;
	while ((c = ReadKeyboard()) != EOF)
		if (dev == printer)
			WritePrinter(c);
		else
			WriteDisk(c);
	}
A pesar de la introducción de una nueva dependencia WriteDisk, la situación no mejoró (sino empeoró) porque se violó otro principio: “las entidades de software, es decir, clases, módulos, funciones, etc., deben estar abiertas a la extensión, pero cerradas a la extensión”. modificación." Martin explica que estas nuevas declaraciones condicionales if/else reducen la estabilidad y flexibilidad del código. La solución es invertir las dependencias para que los métodos de escritura y lectura dependan del archivo Copy. En lugar de "hacer estallar" las dependencias, se pasan a través del constructor. El código modificado se ve así:
class Reader
	{
		public:
		virtual int Read() = 0;
	};
	class Writer
	{
		public:
		virtual void Write(char) = 0;
	};
	void Copy(Reader& r, Writer& w)
	{
		int c;
		while((c=r.Read()) != EOF)
		w.Write(c);
	}
Ahora la clase Copyse puede reutilizar fácilmente con diferentes implementaciones de métodos de clase Readery Writer. La clase Copyno tiene información sobre la estructura interna de los tipos Readery Writer, por lo que es posible reutilizarlos con diferentes implementaciones. Pero si todo esto le parece una especie de palabrería, tal vez los siguientes ejemplos en Java y C# aclaren la situación.

Ejemplo en Java y C#

Para ilustrar la facilidad de la inyección de dependencias sin un contenedor de dependencias, comencemos con un ejemplo simple que se puede personalizar para su uso DIen solo unos pocos pasos. Digamos que tenemos una clase HtmlUserPresentationque, cuando se llaman sus métodos, genera una interfaz de usuario HTML. He aquí un ejemplo sencillo:
HtmlUserPresentation htmlUserPresentation = new HtmlUserPresentation();
String table = htmlUserPresentation.createTable(rowTableVals, "Login Error Status");
Cualquier proyecto que utilice este código de clase dependerá de la clase HtmlUserPresentation, lo que dará lugar a los problemas de usabilidad y mantenimiento descritos anteriormente. Inmediatamente se sugiere una mejora: crear una interfaz con firmas de todos los métodos disponibles actualmente en la clase HtmlUserPresentation. A continuación se muestra un ejemplo de esta interfaz:
public interface IHtmlUserPresentation {
	String createTable(ArrayList rowVals, String caption);
	String createTableRow(String tableCol);
	// Оставшиеся сигнатуры
}
Después de crear la interfaz, modificamos la clase HtmlUserPresentationpara usarla. Volviendo a crear instancias del tipo HtmlUserPresentation, ahora podemos usar el tipo de interfaz en lugar del tipo base:
IHtmlUserPresentation htmlUserPresentation = new HtmlUserPresentation();
String table = htmlUserPresentation.createTable(rowTableVals, "Login Error Status");
La creación de una interfaz nos permite utilizar fácilmente otras implementaciones de IHtmlUserPresentation. Por ejemplo, si queremos probar este tipo, podemos reemplazar fácilmente el tipo base HtmlUserPresentationcon otro tipo llamado HtmlUserPresentationTest. Los cambios realizados hasta ahora hacen que el código sea más fácil de probar, mantener y escalar, pero no contribuyen a su reutilización, ya que todas las HtmlUserPresentationclases que utilizan el tipo aún conocen su existencia. Para eliminar esta dependencia directa, puedes pasar un tipo de interfaz IHtmlUserPresentational constructor (o lista de parámetros del método) de la clase o método que lo usará:
public UploadFile(IHtmlUserPresentation htmlUserPresentation)
El constructor UploadFileahora tiene acceso a toda la funcionalidad del tipo IHtmlUserPresentation, pero no sabe nada sobre la estructura interna de la clase que implementa esta interfaz. En este contexto, la inyección de tipos ocurre cuando se crea una instancia de la clase UploadFile. Un tipo de interfaz IHtmlUserPresentationse vuelve reutilizable al pasar diferentes implementaciones a diferentes clases o métodos que requieren diferentes funcionalidades.

Conclusión y recomendaciones para consolidar el material.

Aprendió sobre la inyección de dependencia y que se dice que las clases dependen directamente entre sí cuando una de ellas crea una instancia de otra para obtener acceso a la funcionalidad del tipo de destino. Para desacoplar la dependencia directa entre los dos tipos, debes crear una interfaz. Una interfaz le da a un tipo la capacidad de incluir diferentes implementaciones, según el contexto de la funcionalidad requerida. Al pasar un tipo de interfaz a un método o constructor de clase, la clase/método que necesita la funcionalidad no conoce ningún detalle sobre el tipo que implementa la interfaz. Debido a esto, un tipo de interfaz se puede reutilizar en diferentes clases que requieren un comportamiento similar, pero no idéntico.
  • Para experimentar con la inyección de dependencia, mire su código de una o más aplicaciones e intente convertir un tipo base muy utilizado en una interfaz.

  • Cambie las clases que crean instancias directas de este tipo base para usar este nuevo tipo de interfaz y páselo a través del constructor o la lista de parámetros del método de clase que lo usará.

  • Cree una implementación de prueba para probar este tipo de interfaz. Una vez que su código sea refactorizado, DIserá más fácil de implementar y notará cuánto más flexible se vuelve su aplicación en términos de reutilización y mantenibilidad.
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION