Preguntas frecuentes sobre DI (FAQ)

¿Es DI otro nombre para IoC?

La Inversion of Control (IoC) es un principio centrado en la forma en que se ejecuta el código: si tu código inicia código externo o si tu código se integra en código externo, que luego lo llama. IoC es un concepto amplio que incluye eventos, el llamado Principio de Hollywood y otros aspectos. Las fábricas, que forman parte de la Regla #3: Deja que la Fábrica se encargue, y representan la inversión para el operador new, también son componentes de este concepto.

La Dependency Injection (DI) trata sobre cómo un objeto sabe de otro objeto, es decir, la dependencia. Es un patrón de diseño que requiere el paso explícito de dependencias entre objetos.

Por lo tanto, se puede decir que DI es una forma específica de IoC. Sin embargo, no todas las formas de IoC son adecuadas en términos de pureza del código. Por ejemplo, entre los anti-patrones, incluimos todas las técnicas que trabajan con estado global o el llamado Service Locator.

¿Qué es un Service Locator?

Un Localizador de Servicios es una alternativa a la Inyección de Dependencias. Funciona creando un almacén central donde se registran todos los servicios o dependencias disponibles. Cuando un objeto necesita una dependencia, la solicita al Localizador de Servicios.

Sin embargo, en comparación con la Inyección de Dependencias, pierde transparencia: las dependencias no se pasan directamente a los objetos y, por tanto, no son fácilmente identificables, lo que requiere examinar el código para descubrir y comprender todas las conexiones. Las pruebas también son más complicadas, ya que no podemos simplemente pasar objetos simulados a los objetos probados, sino que tenemos que pasar por el Localizador de Servicios. Además, el Localizador de Servicios altera el diseño del código, ya que los objetos individuales deben ser conscientes de su existencia, lo que difiere de la Inyección de Dependencias, donde los objetos no tienen conocimiento del contenedor DI.

¿Cuándo es mejor no utilizar DI?

No se conocen dificultades asociadas al uso del patrón de diseño Inyección de Dependencias. Por el contrario, la obtención de dependencias desde ubicaciones globalmente accesibles conlleva una serie de complicaciones, al igual que el uso de un Localizador de Servicios. Por lo tanto, es aconsejable utilizar siempre DI. No se trata de un enfoque dogmático, sino que simplemente no se ha encontrado una alternativa mejor.

Sin embargo, hay ciertas situaciones en las que no nos pasamos objetos y los obtenemos del espacio global. Por ejemplo, al depurar código y necesitar volcar el valor de una variable en un punto concreto del programa, medir la duración de cierta parte del programa o registrar un mensaje. En estos casos, en los que se trata de acciones temporales que más tarde se eliminarán del código, es legítimo utilizar un dumper, cronómetro o logger accesible globalmente. Al fin y al cabo, estas herramientas no pertenecen al diseño del código.

¿Tiene sus inconvenientes el uso de DI?

¿El uso de la inyección de dependencias implica alguna desventaja, como una mayor complejidad en la escritura de código o un peor rendimiento? ¿Qué perdemos cuando empezamos a escribir código de acuerdo con DI?

DI no tiene ningún impacto en el rendimiento de la aplicación ni en los requisitos de memoria. El rendimiento del contenedor DI puede influir, pero en el caso de Nette DI, el contenedor se compila en PHP puro, por lo que su sobrecarga durante el tiempo de ejecución de la aplicación es esencialmente nula.

Al escribir código, es necesario crear constructores que acepten dependencias. En el pasado, esto podía llevar mucho tiempo, pero gracias a los IDEs modernos y a la promoción de propiedades de los constructores, ahora es cuestión de unos segundos. Las fábricas se pueden generar fácilmente utilizando Nette DI y un plugin de PhpStorm con sólo unos clics. Por otra parte, no hay necesidad de escribir singletons y puntos de acceso estáticos.

Se puede concluir que una aplicación correctamente diseñada usando DI no es ni más corta ni más larga comparada con una aplicación usando singletons. Las partes del código que trabajan con dependencias simplemente se extraen de las clases individuales y se trasladan a nuevas ubicaciones, es decir, el contenedor DI y las fábricas.

¿Cómo reescribir una aplicación heredada a DI?

La migración de una aplicación heredada a Inyección de Dependencias puede ser un proceso difícil, especialmente para aplicaciones grandes y complejas. Es importante abordar este proceso de forma sistemática.

  • Al pasar a la inyección de dependencias, es importante que todos los miembros del equipo comprendan los principios y prácticas que se están utilizando.
  • En primer lugar, realice un análisis de la aplicación existente para identificar los componentes clave y sus dependencias. Cree un plan sobre qué partes se refactorizarán y en qué orden.
  • Implemente un contenedor DI o, mejor aún, utilice una biblioteca existente como Nette DI.
  • Refactorizar gradualmente cada parte de la aplicación para utilizar la inyección de dependencias. Esto puede implicar modificar constructores o métodos para que acepten dependencias como parámetros.
  • Modificar los lugares del código donde se crean los objetos de dependencia para que las dependencias sean inyectadas por el contenedor. Esto puede incluir el uso de fábricas.

Recuerde que pasar a la inyección de dependencias es una inversión en la calidad del código y la sostenibilidad a largo plazo de la aplicación. Aunque puede ser difícil hacer estos cambios, el resultado debería ser un código más limpio, más modular y fácil de probar que esté listo para futuras extensiones y mantenimiento.

¿Por qué se prefiere la composición a la herencia?

Es preferible utilizar la composición en lugar de la herencia porque sirve para reutilizar código sin tener que preocuparse por las consecuencias de los cambios. Así, proporciona un acoplamiento más laxo en el que no tenemos que preocuparnos de que cambiar algún código provoque la necesidad de cambiar otro código dependiente. Un ejemplo típico es una situación conocida como el infierno de los constructores.

¿Se puede utilizar Nette DI Container fuera de Nette?

Por supuesto. El Nette DI Container es parte de Nette, pero está diseñado como una librería independiente que puede ser usada independientemente de otras partes del framework. Simplemente instálelo usando Composer, cree un archivo de configuración definiendo sus servicios, y luego use unas pocas líneas de código PHP para crear el contenedor DI. E inmediatamente puedes empezar a aprovechar las ventajas de la Inyección de Dependencias en tus proyectos.

El capítulo Nette DI Container describe cómo es un caso de uso específico, incluyendo el código.

¿Por qué la configuración está en archivos NEON?

NEON es un lenguaje de configuración sencillo y fácilmente legible desarrollado dentro de Nette para configurar aplicaciones, servicios y sus dependencias. Comparado con JSON o YAML, ofrece opciones mucho más intuitivas y flexibles para este propósito. En NEON, puedes describir de forma natural enlaces que no sería posible escribir en Symfony y YAML en absoluto o sólo a través de una descripción compleja.

¿El análisis de los archivos NEON ralentiza la aplicación?

Aunque los archivos NEON se analizan muy rápidamente, este aspecto no importa realmente. La razón es que el análisis sintáctico de los archivos se produce sólo una vez durante el primer lanzamiento de la aplicación. Después de eso, el código contenedor DI se genera, se almacena en el disco, y se ejecuta para cada solicitud posterior sin necesidad de más análisis.

Así es como funciona en un entorno de producción. Durante el desarrollo, los archivos NEON se analizan cada vez que cambia su contenido, lo que garantiza que el desarrollador siempre disponga de un contenedor DI actualizado. Como se mencionó anteriormente, el análisis sintáctico real es cuestión de un instante.

¿Cómo puedo acceder a los parámetros del archivo de configuración en mi clase?

Ten en cuenta la Regla nº 1: Déjalo que te lo pasen. Si una clase requiere información de un fichero de configuración, no necesitamos averiguar cómo acceder a esa información; en su lugar, simplemente la pedimos – por ejemplo, a través del constructor de la clase. Y realizamos el paso en el archivo de configuración.

En este ejemplo, %myParameter% es un marcador de posición para el valor del parámetro myParameter, que se pasará al constructor MyClass:

# config.neon
parameters:
	myParameter: Some value

services:
	- MyClass(%myParameter%)

Si quieres pasar múltiples parámetros o usar autocableado, es útil envolver los parámetros en un objeto.

¿Admite Nette la interfaz PSR-11 Container?

Nette DI Container no soporta PSR-11 directamente. Sin embargo, si necesita interoperabilidad entre el Nette DI Container y las librerías o frameworks que esperan el PSR-11 Container Interface, puede crear un adaptador simple que sirva de puente entre el Nette DI Container y el PSR-11.

versión: 3.x