Preguntas frecuentes sobre DI (FAQ)

¿Es DI otro nombre para IoC?

Inversion of Control (IoC) es un principio centrado en la forma en que se ejecuta el código: si su código ejecuta código ajeno o si su código se integra en código ajeno, que luego lo llama. IoC es un término amplio que incluye eventos, el llamado Principio de Hollywood y otros aspectos. Parte de este concepto también son las fábricas, de las que habla la Regla n.º 3: déjalo en manos de la fábrica, y que representan una inversión para el operador new.

Dependency Injection (DI) se centra en la forma en que un objeto conoce a otro objeto, es decir, sus dependencias. 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 desde el punto de vista de la limpieza del código. Por ejemplo, entre los antipatrones se encuentran técnicas que trabajan con estado global o el llamado Service Locator.

¿Qué es Service Locator?

Es una alternativa a la Inyección de Dependencias. Funciona creando un repositorio central donde se registran todos los servicios o dependencias disponibles. Cuando un objeto necesita una dependencia, la solicita al Service Locator.

Sin embargo, en comparación con la Inyección de Dependencias, pierde transparencia: las dependencias no se pasan directamente a los objetos y no son tan fáciles de identificar, lo que requiere examinar el código para revelar y comprender todas las conexiones. Las pruebas también son más complicadas porque no podemos simplemente pasar objetos simulados (mock) a los objetos probados, sino que debemos hacerlo a través del Service Locator. Además, el Service Locator interrumpe el diseño del código, ya que los objetos individuales deben conocer 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 usar DI?

No se conocen dificultades asociadas con el uso del patrón de diseño de Inyección de Dependencias. Por el contrario, obtener dependencias de lugares globalmente disponibles conduce a toda una serie de complicaciones, al igual que el uso de Service Locator. Por lo tanto, es aconsejable usar DI siempre. Esto no es un enfoque dogmático, sino simplemente que no se ha encontrado una alternativa mejor.

Sin embargo, existen ciertas situaciones en las que no pasamos objetos y los obtenemos del espacio global. Por ejemplo, al depurar código, cuando necesita imprimir el valor de una variable en un punto específico del programa, medir la duración de una parte específica del programa o registrar un mensaje. En tales casos, cuando se trata de tareas temporales que luego se eliminarán del código, es legítimo utilizar un dumper, cronómetro o logger globalmente disponible. Estas herramientas no pertenecen al diseño del código.

¿Tiene el uso de DI sus desventajas?

¿Implica el uso de la Inyección de Dependencias alguna desventaja, como una mayor dificultad para escribir código o un peor rendimiento? ¿Qué perdemos cuando empezamos a escribir código de acuerdo con DI?

DI no tiene impacto en el rendimiento ni en los requisitos de memoria de la aplicación. El rendimiento del Contenedor DI puede jugar un cierto papel, sin embargo, en el caso de Nette DI, el contenedor se compila en PHP puro, por lo que su sobrecarga durante la ejecución de la aplicación es esencialmente nula.

Al escribir código, suele ser necesario crear constructores que acepten dependencias. Antes esto podía ser tedioso, pero gracias a los IDE modernos y la promoción de propiedades del constructor, ahora es cuestión de segundos. Las fábricas se pueden generar fácilmente usando Nette DI y el plugin para PhpStorm con un clic del ratón. Por otro lado, desaparece la necesidad de escribir singletons y puntos de acceso estáticos.

Se puede afirmar que una aplicación correctamente diseñada que utiliza DI no es ni más corta ni más larga en comparación con una aplicación que utiliza singletons. Las partes del código que trabajan con dependencias simplemente se extraen de las clases individuales y se mueven a nuevos lugares, es decir, al contenedor DI y a las fábricas.

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

La transición de una aplicación legacy a la Inyección de Dependencias puede ser un proceso desafiante, especialmente para aplicaciones grandes y complejas. Es importante abordar este proceso sistemáticamente.

  • Al pasar a la Inyección de Dependencias, es importante que todos los miembros del equipo comprendan los principios y procedimientos que se utilizan.
  • Primero, realice un análisis de la aplicación existente e identifique 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, use una biblioteca existente, como Nette DI.
  • Refactorice gradualmente partes individuales de la aplicación para usar la Inyección de Dependencias. Esto puede incluir modificar constructores o métodos para que acepten dependencias como parámetros.
  • Modifique los lugares en el código donde se crean objetos con dependencias para que, en su lugar, las dependencias sean inyectadas por el contenedor. Esto puede incluir el uso de fábricas.

Recuerde que la transición a la Inyección de Dependencias es una inversión en la calidad del código y la mantenibilidad a largo plazo de la aplicación. Aunque puede ser desafiante realizar estos cambios, el resultado debería ser un código más limpio, modular y fácilmente comprobable, listo para futuras expansiones y mantenimiento.

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

Es preferible usar la composición en lugar de la herencia, porque sirve para reutilizar código sin tener que preocuparnos por las consecuencias de los cambios. Proporciona, por tanto, un acoplamiento más flexible, donde no tenemos que temer que un cambio en algún código provoque la necesidad de cambiar otro código dependiente. Un ejemplo típico es la situación conocida como constructor hell.

¿Se puede usar Nette DI Container fuera de Nette?

Definitivamente. Nette DI Container es parte de Nette, pero está diseñado como una biblioteca independiente que se puede usar independientemente de otras partes del framework. Simplemente instálelo usando Composer, cree un archivo de configuración con la definición de sus servicios y luego, usando unas pocas líneas de código PHP, cree el contenedor DI. Y puede comenzar a aprovechar de inmediato las ventajas de la Inyección de Dependencias en sus proyectos.

Cómo se ve el uso concreto, incluidos los códigos, se describe en el capítulo Nette DI Container.

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

NEON es un lenguaje de configuración simple y fácil de leer que se desarrolló dentro de Nette para configurar aplicaciones, servicios y sus dependencias. En comparación con JSON o YAML, ofrece opciones mucho más intuitivas y flexibles para este propósito. En NEON, se pueden describir naturalmente las relaciones que en Symfony & YAML serían imposibles de escribir o solo a través de una descripción compleja.

¿No ralentiza la aplicación el análisis de archivos NEON?

Aunque los archivos NEON se analizan muy rápidamente, este aspecto no importa en absoluto. La razón es que el análisis de archivos solo ocurre una vez cuando la aplicación se inicia por primera vez. Luego, se genera el código del contenedor DI, se guarda en el disco y se ejecuta en cada solicitud posterior, sin necesidad de realizar 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, para que el desarrollador siempre tenga un contenedor DI actualizado. El análisis en sí, como se dijo, es cuestión de un momento.

¿Cómo accedo desde mi clase a los parámetros en el archivo de configuración?

Tengamos en cuenta la Regla n.º 1: haz que te lo pasen. Si una clase requiere información del archivo de configuración, no tenemos que pensar en cómo acceder a esa información, sino que simplemente la solicitamos, 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 pasa al constructor de la clase MyClass:

# config.neon
parameters:
	myParameter: Some value

services:
	- MyClass(%myParameter%)

Si desea pasar múltiples parámetros o usar autowiring, es aconsejable envolver los parámetros en un objeto.

¿Soporta Nette PSR-11: Container interface?

Nette DI Container no soporta PSR-11 directamente. Sin embargo, si necesita interoperabilidad entre Nette DI Container y bibliotecas o frameworks que esperan la Interfaz de Contenedor PSR-11, puede crear un adaptador simple que sirva como puente entre Nette DI Container y PSR-11.

versión: 3.x