¿Cuáles son las desventajas de utilizar la inyección de dependencia? [cerrado]
Estoy intentando introducir la inyección de dependencia (DI) como patrón aquí en el trabajo y uno de nuestros desarrolladores principales quisiera saber: ¿Cuáles son, si las hay, las desventajas de usar el patrón de inyección de dependencia ?
Tenga en cuenta que estoy buscando aquí una lista, si es posible, exhaustiva, no una discusión subjetiva sobre el tema.
Aclaración : estoy hablando del patrón de inyección de dependencia (consulte este artículo de Martin Fowler), no de un marco específico, ya sea basado en XML (como Spring ) o en código (como Guice ), o "autoenrollado". .
Se está llevando a cabo una gran discusión/despotricar/debate en el subreddit /r/programming de Reddit .
Un par de puntos:
- DI aumenta la complejidad, normalmente aumentando el número de clases ya que las responsabilidades están más separadas, lo que no siempre es beneficioso
- Su código estará (en cierto modo) acoplado al marco de inyección de dependencia que utilice (o, de manera más general, a cómo decida implementar el patrón DI)
- Los contenedores DI o los enfoques que realizan resolución de tipos generalmente incurren en una ligera penalización de tiempo de ejecución (muy insignificante, pero está ahí)
Generalmente, el beneficio del desacoplamiento hace que cada tarea sea más sencilla de leer y comprender, pero aumenta la complejidad de orquestar las tareas más complejas.
El mismo problema básico que suele surgir con la programación orientada a objetos, las reglas de estilo y casi todo lo demás. Es posible (de hecho, muy común) hacer demasiada abstracción, agregar demasiada dirección indirecta y, en general, aplicar buenas técnicas en exceso y en los lugares equivocados.
Cada patrón u otra construcción que apliques aporta complejidad. La abstracción y la indirección dispersan la información, a veces eliminando detalles irrelevantes, pero también a veces dificultan la comprensión de lo que está sucediendo exactamente. Cada regla que se aplica conlleva inflexibilidad y descarta opciones que podrían ser el mejor enfoque.
El objetivo es escribir código que haga el trabajo y que sea sólido, legible y mantenible. Usted es un desarrollador de software, no un constructor de torres de marfil.
Enlaces relevantes
El efecto de plataforma interna
No dejes que los astronautas de la arquitectura te asusten
Probablemente la forma más simple de inyección de dependencia (no te rías) es un parámetro. El código dependiente depende de los datos, y esos datos se inyectan mediante el paso del parámetro.
Sí, es tonto y no aborda el punto de inyección de dependencia orientado a objetos, pero un programador funcional le dirá que (si tiene funciones de primera clase) este es el único tipo de inyección de dependencia que necesita. El punto aquí es tomar un ejemplo trivial y mostrar los problemas potenciales.
Tomemos esta simple función tradicional. La sintaxis de C++ no es importante aquí, pero tengo que escribirla de alguna manera...
void Say_Hello_World ()
{
std::cout << "Hello World" << std::endl;
}
Tengo una dependencia que quiero extraer e inyectar: el texto "Hola mundo". Suficientemente fácil...
void Say_Something (const char *p_text)
{
std::cout << p_text << std::endl;
}
¿Cómo es eso más inflexible que el original? Bueno, ¿qué pasa si decido que la salida debe ser Unicode? Probablemente quiera cambiar de std::cout a std::wcout . Pero eso significa que mis cadenas tienen que ser de *wchar_*t, no de char . O se debe cambiar cada persona que llama o (más razonablemente), la implementación anterior se reemplaza con un adaptador que traduce la cadena y llama a la nueva implementación.
Eso es un trabajo de mantenimiento que no sería necesario si hubiéramos conservado el original.
Y si parece trivial, eche un vistazo a esta función del mundo real de la API de Win32...
Función CreateWindowExA (winuser.h)
Son 12 "dependencias" con las que lidiar. Por ejemplo, si las resoluciones de pantalla se vuelven realmente enormes, tal vez necesitemos valores de coordenadas de 64 bits y otra versión de CreateWindowEx. Y sí, ya hay una versión anterior todavía dando vueltas, que presumiblemente se asigna a la versión más nueva detrás de escena...
Crear ventana una macro (winuser.h)
Esas "dependencias" no son sólo un problema para el desarrollador original: todos los que usan esa interfaz tienen que buscar cuáles son las dependencias, cómo se especifican y qué significan, y decidir qué hacer con su aplicación. Aquí es donde las palabras "valores predeterminados sensatos" pueden simplificar mucho la vida.
La inyección de dependencia orientada a objetos no es diferente en principio. Escribir una clase es una sobrecarga, tanto en el texto del código fuente como en el tiempo del desarrollador, y si esa clase se escribe para proporcionar dependencias de acuerdo con algunas especificaciones de objetos dependientes, entonces el objeto dependiente está bloqueado para soportar esa interfaz, incluso si es necesario. para reemplazar la implementación de ese objeto.
Nada de esto debe interpretarse como una afirmación de que la inyección de dependencia es mala, ni mucho menos. Pero cualquier buena técnica puede aplicarse en exceso y en el lugar equivocado. Así como no es necesario extraer todas las cadenas y convertirlas en parámetros, no es necesario extraer todos los comportamientos de bajo nivel de objetos de alto nivel y convertirlos en una dependencia inyectable.
Aquí está mi reacción inicial: Básicamente, las mismas desventajas de cualquier patrón.
- se necesita tiempo para aprender
- Si se malinterpreta, puede provocar más daño que bien.
- Si se lleva al extremo, puede suponer más trabajo del que justificaría el beneficio.
La mayor "inconveniente" de la inversión de control (no del todo DI, pero sí lo suficientemente cercana) es que tiende a eliminar tener un único punto para ver una descripción general de un algoritmo. Sin embargo, eso es básicamente lo que sucede cuando se ha desacoplado el código: la capacidad de buscar en un solo lugar es un artefacto de acoplamiento estrecho.
He estado usando Guice (marco Java DI) ampliamente durante los últimos 6 meses. Si bien en general creo que es genial (especialmente desde la perspectiva de las pruebas), existen ciertas desventajas. Más destacado:
- El código puede volverse más difícil de entender. La inyección de dependencia se puede utilizar de maneras muy... creativas. Por ejemplo, acabo de encontrar un código que utiliza una anotación personalizada para inyectar ciertos IOStreams (por ejemplo: @Server1Stream, @Server2Stream). Si bien esto funciona, y admito que tiene cierta elegancia, hace que comprender las inyecciones de Guice sea un requisito previo para comprender el código.
- Mayor curva de aprendizaje al aprender proyecto. Esto está relacionado con el punto 1. Para comprender cómo funciona un proyecto que utiliza la inyección de dependencia, es necesario comprender tanto el patrón de inyección de dependencia como el marco específico. Cuando comencé en mi trabajo actual, pasé bastantes horas confusas asimilando lo que Guice estaba haciendo detrás de escena.
- Los constructores se vuelven grandes. Aunque esto se puede resolver en gran medida con un constructor predeterminado o una fábrica.
- Los errores se pueden ofuscar. Mi ejemplo más reciente de esto fue que tuve una colisión con 2 nombres de banderas. Guice se tragó el error en silencio y una de mis banderas no se inicializó.
- Los errores se envían al tiempo de ejecución. Si configura su módulo Guice incorrectamente (referencia circular, enlace incorrecto, ...), la mayoría de los errores no se descubren durante el tiempo de compilación. En cambio, los errores quedan expuestos cuando el programa realmente se ejecuta.
Ahora que me he quejado. Permítanme decirles que continuaré usando (de buena gana) Guice en mi proyecto actual y probablemente en el próximo. La inyección de dependencia es un patrón excelente e increíblemente poderoso. Pero definitivamente puede resultar confuso y es casi seguro que pasará algún tiempo maldiciendo cualquier marco de inyección de dependencia que elija.
Además, estoy de acuerdo con otros carteles en que la inyección de dependencia se puede abusar.