Que se sepa que soy un gran admirador de la inyección de dependencia (DI) y las pruebas automatizadas. Podría hablar todo el día al respecto.
Antecedentes
Recientemente, nuestro equipo acaba de recibir este gran proyecto que se construirá desde cero. Es una aplicación estratégica con requisitos comerciales complejos. Por supuesto, quería que fuera agradable y limpio, lo que para mí significaba: mantenible y comprobable. Entonces quería usar DI.
Resistencia
El problema estaba en nuestro equipo, DI es tabú. Se ha mencionado varias veces, pero los dioses no lo aprueban. Pero eso no me desanimó.
Mi movimiento
Esto puede sonar extraño, pero las bibliotecas de terceros generalmente no están aprobadas por nuestro equipo de arquitectos (piense: "no hablarás de Unity , Ninject , NHibernate , Moq o NUnit , para que no te corte el dedo"). Entonces, en lugar de usar un contenedor DI establecido, escribí un contenedor extremadamente simple. Básicamente conectó todas sus dependencias al inicio, inyecta cualquier dependencia (constructor / propiedad) y eliminó los objetos desechables al final de la solicitud web. Era extremadamente liviano e hizo lo que necesitábamos. Y luego les pedí que lo revisaran.
La respuesta
Bueno, para abreviar. Me encontré con una fuerte resistencia. El argumento principal fue: "No necesitamos agregar esta capa de complejidad a un proyecto ya complejo". Además, "No es como si conectaremos diferentes implementaciones de componentes". Y "Queremos que sea simple, si es posible, simplemente coloque todo en un solo ensamblaje. DI es una complejidad innecesaria sin beneficio".
Finalmente mi pregunta
¿Cómo manejarías mi situación? No soy bueno para presentar mis ideas, y me gustaría saber cómo la gente presentaría sus argumentos.
Por supuesto, supongo que, como yo, prefieres usar DI. Si no está de acuerdo, explique por qué para que pueda ver el otro lado de la moneda. Sería realmente interesante ver el punto de vista de alguien que no está de acuerdo.
Actualizar
Gracias por las respuestas de todos. Realmente pone las cosas en perspectiva. Es lo suficientemente bueno como para tener otro par de ojos que te den retroalimentación, ¡quince es realmente increíble! Estas son respuestas realmente geniales y me ayudaron a ver el problema desde diferentes lados, pero solo puedo elegir una respuesta, por lo que elegiré la que haya sido votada mejor. Gracias a todos por tomarse el tiempo para responder.
He decidido que probablemente no sea el mejor momento para implementar DI, y no estamos preparados para ello. En cambio, concentraré mis esfuerzos en hacer que el diseño sea comprobable e intentaré presentar pruebas unitarias automatizadas. Soy consciente de que escribir pruebas es una sobrecarga adicional y si alguna vez se decide que la sobrecarga adicional no vale la pena, personalmente aún lo vería como una situación ganadora, ya que el diseño aún es comprobable. Y si alguna vez la prueba o DI es una opción en el futuro, el diseño puede manejarlo fácilmente.
Respuestas:
Tomando un par de argumentos en contra:
Lo que quiere es que el sistema sea comprobable. Para que sea fácilmente comprobable, debe buscar burlarse de las diversas capas del proyecto (base de datos, comunicaciones, etc.) y, en este caso , conectará diferentes implementaciones de componentes.
Venda DI en los beneficios de prueba que le brinda. Si el proyecto es complejo, necesitará pruebas unitarias buenas y sólidas.
Otro beneficio es que, a medida que codifica para interfaces, si encuentra una mejor implementación (más rápida, con menos memoria, lo que sea) de uno de sus componentes, el uso de DI hace que sea mucho más fácil cambiar la implementación anterior por la nuevo.
Lo que digo aquí es que debe abordar los beneficios que brinda la DI en lugar de abogar por la DI por el bien de la DI. Al hacer que las personas estén de acuerdo con la declaración:
Entonces cambias el problema. Debe asegurarse de que DI es la respuesta a esta declaración. Al hacerlo, sus compañeros de trabajo serán dueños de la solución en lugar de sentir que se les ha impuesto.
fuente
Por lo que está escribiendo, no DI en sí es tabú, sino el uso de contenedores DI, que es una cosa diferente. Supongo que no tendrás problemas con tu equipo cuando solo escribas componentes independientes que obtengan otros componentes que deben pasar, por ejemplo, por el constructor. Simplemente no lo llames "DI".
Puede pensar que dichos componentes no usan un contenedor DI es tedioso, y tiene razón, pero dé a su equipo el tiempo para descubrirlo por sí mismos.
fuente
Como me lo pidió, me pondré del lado de su equipo contra DI.
El caso contra la inyección de dependencia
Hay una regla que llamo "Conservación de la Complejidad" que es análoga a la regla en física llamada "Conservación de la Energía". Establece que ninguna cantidad de ingeniería puede reducir la complejidad total de una solución óptima a un problema, todo lo que puede hacer es cambiar esa complejidad. Los productos de Apple no son menos complejos que los de Microsoft, simplemente trasladan la complejidad del espacio del usuario al espacio de ingeniería. (Sin embargo, es una analogía defectuosa. Si bien no se puede crear energía, los ingenieros tienen la mala costumbre de crear complejidad a cada paso. De hecho, muchos programadores parecen disfrutar agregando complejidad y usando magia, que por definición es una complejidad que el programador no comprende completamente. Este exceso de complejidad se puede reducir.) Por lo general, lo que parece reducir la complejidad es simplemente trasladar la complejidad a otro lugar, generalmente a una biblioteca.
Del mismo modo, DI no reduce la complejidad de vincular objetos de cliente con objetos de servidor. Lo que hace es sacarlo de Java y moverlo a XML. Eso es bueno porque centraliza todo en uno (o algunos) archivos XML donde es fácil ver todo lo que está sucediendo y donde es posible cambiar las cosas sin volver a compilar el código. Por otro lado, eso es malo porque los archivos XML no son Java y, por lo tanto, no están disponibles para las herramientas Java. No puede depurarlos en el depurador, no puede utilizar el análisis estático para rastrear a través de su jerarquía de llamadas, y así sucesivamente. En particular, los errores en el marco DI se vuelven extremadamente difíciles de detectar, identificar y corregir.
Por lo tanto, es mucho más difícil demostrar que un programa es correcto cuando se utiliza DI. Mucha gente argumenta que los programas que usan DI son más fáciles de probar , pero las pruebas de programa nunca pueden probar la ausencia de errores . (Tenga en cuenta que el documento vinculado tiene alrededor de 40 años y también tiene algunas referencias arcanas y cita algunas prácticas desactualizadas, pero muchas de las declaraciones básicas aún son ciertas. En particular, que P <= p ^ n, donde P es la probabilidad de que el sistema esté libre de errores, p es la probabilidad de que un componente del sistema esté libre de errores yn es el número de componentes. Por supuesto, esta ecuación está demasiado simplificada, pero apunta a una verdad importante). El bloqueo de NASDAQ durante la IPO de Facebook es un ejemplo reciente, dondeAfirmaron que habían pasado 1,000 horas probando el código y aún no descubrieron los errores que causaron el accidente. 1,000 horas es media persona-año, por lo que no es como si estuvieran escatimando en las pruebas.
Es cierto que casi nadie se compromete (y mucho menos completa) la tarea de probar formalmente que cualquier código es correcto, pero incluso los intentos débiles de prueba superan a la mayoría de los regímenes de prueba para encontrar errores. Intentos de prueba, por ejemplo , L4.verified , descubren invariablemente una gran cantidad de errores que las pruebas no detectaron y probablemente nunca descubrirían a menos que el probador ya conociera la naturaleza exacta del error. Cualquier cosa que haga que el código sea más difícil de verificar mediante inspección puede verse como una reducción de la posibilidad de que se detecten errores , incluso si aumenta la capacidad de prueba.
Además, no se requiere DI para las pruebas . Raramente lo uso para ese propósito, ya que prefiero probar el sistema contra el software real en el sistema en lugar de alguna versión de prueba alternativa. Todo lo que cambio en la prueba se almacena (o bien podría) almacenarse en archivos de propiedades que dirigen el programa a los recursos en el entorno de prueba en lugar de la producción. Prefiero pasar el tiempo configurando un servidor de base de datos de prueba con una copia de la base de datos de producción que pasar el tiempo codificando una base de datos simulada, porque de esta manera podré rastrear problemas con los datos de producción (los problemas de codificación de caracteres son solo un ejemplo ) y errores de integración del sistema, así como errores en el resto del código.
La inyección de dependencia tampoco es necesaria para la inversión de dependencia . Puede utilizar fácilmente métodos de fábrica estáticos para implementar la Inversión de dependencias. Así es, de hecho, cómo se hizo en la década de 1990.
En verdad, aunque he estado usando DI durante una década, las únicas ventajas que ofrece que me parecen convincentes son la capacidad de configurar bibliotecas de terceros para usar otras bibliotecas de terceros (por ejemplo, configurar Spring para usar Hibernate y ambas para usar Log4J ) y la habilitación de IoC a través de proxies. Si no está utilizando bibliotecas de terceros y no está utilizando IoC, entonces no tiene mucho sentido y, de hecho, agrega complejidad injustificada.
fuente
Lamento decir que el problema que tiene, dado lo que han dicho sus compañeros de trabajo en su pregunta, es de naturaleza política más que técnica. Hay dos mantras que debes tener en cuenta:
Claro, puede sacar muchos argumentos en contra con razones técnicas y beneficios sólidos, pero eso lo pondrá en una mala situación con el resto de la compañía. Ya tienen una postura de que no la quieren (porque se sienten cómodos con cómo funcionan las cosas ahora). Por lo tanto, incluso podría dañar su relación con sus colegas si sigue siendo persistente al respecto. Confía en mí en esto porque me ha sucedido y finalmente me despidieron hace varios años (joven e ingenuo que era); Esta es una situación en la que no quieres meterte.
La cuestión es que debe alcanzar cierto nivel de confianza antes de que las personas lleguen a cambiar su forma actual de trabajar. A menos que esté en una posición en la que tenga mucha confianza o pueda decidir este tipo de cosas, como tener el papel de arquitecto o líder tecnológico, es muy poco lo que puede hacer para cambiar a sus colegas. Tomará mucho tiempo con persuasión y esfuerzo (como tomar pequeños pasos de mejora) para ganar confianza y lograr que la cultura de su empresa cambie. Estamos hablando de un intervalo de tiempo de muchos meses o años.
Si encuentra que la cultura actual de la empresa no está funcionando con usted, entonces al menos sabe otra cosa que preguntar en su próxima entrevista de trabajo. ;-)
fuente
No uses DI. Olvídalo.
Cree una implementación del patrón de fábrica GoF que "cree" o "encuentre" sus dependencias particulares y las pase a su objeto y déjelo así, pero no lo llame DI y no cree un marco complejo complejo. Mantenlo simple y relevante. Asegúrese de que las dependencias y sus clases sean tales que sea posible cambiar a DI; pero mantén la fábrica como maestra y mantén un alcance extremadamente limitado.
Con el tiempo, puede esperar que crezca e incluso un día la transición a DI. Deje que las pistas lleguen a la conclusión en su propio tiempo y no empuje. Incluso si nunca llega a eso, al menos su código tiene algunas de las ventajas de DI, por ejemplo, código comprobable por unidad.
fuente
He visto proyectos que llevaron la DI al extremo para probar unidades y simular objetos. Tanto es así que la configuración DI se convirtió en el lenguaje de programación en sí mismo con tanta configuración necesaria para que un sistema se ejecute como el código real.
¿El sistema está sufriendo en este momento debido a la falta de API internas apropiadas que no se pueden probar? ¿Espera que cambiar el sistema a un modelo DI le permita realizar mejores pruebas unitarias? No necesita un contenedor para probar el código de la unidad. El uso de un modelo DI le permitiría realizar pruebas unitarias más fácilmente con los objetos Mock, pero no es obligatorio.
No necesita cambiar todo el sistema para que sea compatible con DI a la vez. Tampoco deberías escribir arbitrariamente pruebas unitarias. Refactorice el código a medida que lo encuentre en sus tickets de funciones y errores. Luego, asegúrese de que el código que ha tocado sea fácilmente comprobable.
Piensa en CodeRot como una enfermedad. No quieres comenzar a piratear arbitrariamente las cosas. Tienes un paciente, ves un punto dolorido, por lo que comienzas a arreglar el lugar y las cosas a su alrededor. Una vez que esa área está limpia, pasa a la siguiente. Tarde o temprano habrás tocado la mayoría del código y no requirió este cambio arquitectónico "masivo".
No me gusta que las pruebas DI y Mock sean mutuamente excluyentes. Y después de ver un proyecto que se fue, por falta de una palabra mejor, loco usando DI, estaría cansado también sin ver el beneficio.
Hay algunos lugares que tiene sentido usar DI:
Exigir a cada desarrollador que sepa dónde está el archivo de configuración DI (me doy cuenta de que los contenedores se pueden instanciar a través del código, pero ¿por qué haría eso?) Cuando quieren realizar un RegEx es frustrante. Ah, veo que hay IRegExCompiler, DefaultRegExCompiler y SuperAwesomeRegExCompiler. Sin embargo, en ningún lugar del código puedo hacer un análisis para ver dónde se usa Default y SuperAwesome.
Mi propia personalidad reaccionaría mejor si alguien me mostrara algo mejor, y luego tratar de convencerme con sus palabras. Hay algo que decir sobre alguien que dedicaría tiempo y esfuerzo para mejorar el sistema. No creo que muchos desarrolladores se preocupen lo suficiente como para que el producto sea realmente mejor. Si pudiera recurrir a ellos con cambios reales en el sistema y mostrarles cómo lo hizo mejor y más fácil para usted, eso vale más que cualquier investigación en línea.
En resumen, la mejor manera de realizar un cambio en un sistema es hacer que el código que está tocando actualmente sea mejor con cada pasada. Tarde o temprano podrás transformar el sistema en algo mejor.
fuente
DI no requiere recipientes invasivos de inversión de control o estructuras de burla. Puede desacoplar el código y permitir DI a través de un diseño simple. Puede realizar pruebas unitarias a través de las capacidades integradas de Visual Studio.
Para ser justos, sus compañeros de trabajo tienen un punto. Usar esas bibliotecas mal (y dado que no están familiarizadas con ellas, habrá problemas de aprendizaje) causará problemas. Al final, el código importa. No arruines tu producto para tus pruebas.
Solo recuerde que el compromiso es necesario en estos escenarios.
fuente
No creo que pueda vender DI más, porque esta es una decisión dogmática (o, como @Spoike lo llamó más cortésmente, político ).
En esta situación, discutir con las mejores prácticas ampliamente aceptadas como los principios de diseño SÓLIDO ya no es posible.
Alguien que tiene más poder político que usted ha tomado una decisión (tal vez incorrecta) de no usar DI.
Por otro lado, usted se ha opuesto a esta decisión al implementar su propio contenedor porque estaba prohibido usar un contenedor establecido. Esta política de hechos consumados que probaste podría haber empeorado la situación.
Análisis
En mi opinión, la DI sin desarrollo impulsado por pruebas (TDD) y pruebas unitarias no tiene un beneficio significativo que supere los costos adicionales iniciales.
¿Es este problema realmente sobre
o se trata más de
[Actualizar]
Si ve su proyecto desde los errores clásicos en la vista de gestión de proyectos , verá
mientras los demás ven
fuente
Si se ve obligado a "vender" un principio a su equipo, entonces probablemente ya haya recorrido varias millas por el camino equivocado.
Nadie quiere hacer un trabajo extra, por lo que si lo que estás tratando de vender les parece un trabajo extra, entonces no vas a convencerlos con Invocación repetida de argumento superior. Necesitan saber que les está mostrando una forma de reducir su carga de trabajo, no aumentarla.
La DI a menudo se ve como una complejidad adicional ahora con la promesa de una posible complejidad reducida más adelante , lo que tiene valor, pero no tanto para alguien que está tratando de reducir su carga de trabajo inicial. Y en muchos casos, DI es solo otra capa de YAGNI . Y el costo de esta complejidad no es cero, en realidad es bastante alto para un equipo que no está acostumbrado a este tipo de patrón. Y se trata de dólares reales que se invierten en un balde que tal vez nunca produzca ningún beneficio.
Póngalo en su lugar.
Su mejor opción es usar DI donde es muy útil, en lugar de solo donde es común. Por ejemplo, muchas aplicaciones usan DI para seleccionar y configurar bases de datos. Y si su aplicación se envía con una capa de base de datos, entonces ese es un lugar tentador para ponerla. Pero si su aplicación se envía con una base de datos invisible para el usuario integrada que de otro modo está estrechamente integrada en el código, entonces las posibilidades de tener que reemplazar la base de datos en tiempo de ejecución se acercan a cero. Y vender DI al decir "mira, podemos hacer fácilmente esto que nunca, nunca, nunca querremos hacer", es probable que te lleve a la lista de "este tipo tiene malas ideas" con tu jefe.
Pero supongamos que tiene una salida de depuración que normalmente se libera IFDEF en el lanzamiento. Y cada vez que un usuario tiene un problema, su equipo de soporte debe enviarle una compilación de depuración para que pueda obtener la salida del registro. Puede usar DI aquí para permitir que el cliente cambie entre controladores de registro:
LogConsole
para desarrolladores,LogNull
para clientes yLogNetwork
para clientes con problemas. Una compilación unificada, tiempo de ejecución configurable.Entonces ni siquiera tiene que llamarlo DI, sino que se llama "la idea realmente buena de Mel" y ahora está en la lista de buenas ideas en lugar de la mala. La gente verá qué tan bien funciona el patrón y comenzará a usarlo donde tenga sentido en su código.
Cuando vendes adecuadamente una idea, la gente no sabrá que los convenciste de nada.
fuente
Por supuesto, la Inversión de control (IoC) tiene una gran cantidad de beneficios: simplifica el código, agrega algo de magia que está haciendo las tareas típicas, etc.
Sin embargo:
Agrega muchas dependencias. La aplicación simple hello world escrita con Spring puede pesar una docena de MB, y he visto que Spring agregó solo para evitar algunas líneas de constructor y setters.
Agrega la magia mencionada anteriormente , que es difícil de entender para los extraños (por ejemplo, los desarrolladores de GUI que necesitan hacer algunos cambios en la capa empresarial). Vea el gran artículo Las mentes innovadoras no piensen igual - New York Times , que describe cómo los expertos subestiman este efecto .
Se endurece para rastrear el uso de clases. IDEs como Eclipse tiene grandes habilidades para rastrear usos de clases dadas o invocaciones de métodos. Pero este rastro se pierde cuando se invoca la inyección de dependencia y la reflexión.
El uso de IoC normalmente no termina con la inyección de dependencias, termina con innumerables proxies, y obtienes el seguimiento de la pila contando cientos de líneas, el 90% de las cuales son proxies e invocaciones mediante reflexión. Complica en gran medida el análisis de seguimiento de pila.
Entonces, como soy un gran admirador de Spring y IoC, recientemente tuve que repensar las preguntas anteriores. Algunos de mis colegas son desarrolladores web tomados dentro del mundo Java del mundo web gobernado por Python y PHP y lo obvio para las cosas javaistas es algo obvio para ellos. Algunos de mis otros colegas están haciendo trucos (por supuesto, indocumentados) que hacen que el error sea muy difícil de encontrar. Por supuesto, el mal uso de IoC les hace las cosas más ligeras;)
Mi consejo, si forzará el uso de IoC en su trabajo, se le hará responsable de cualquier mal uso que alguien más haga;)
fuente
Creo que ChrisF está en algo correcto. No venda DI en sí mismo. Pero venda la idea sobre la unidad que prueba el código.
Un enfoque para obtener un contenedor DI en la arquitectura podría ser dejar que las pruebas conduzcan lentamente la implementación a algo que se adapte bien a esa arquitectura. Por ejemplo, supongamos que está trabajando en un controlador en una aplicación ASP.NET MVC. Digamos que escribes la clase así:
Eso le permite escribir pruebas unitarias desacopladas de la implementación real del repositorio de usuarios. Pero la clase aún funciona sin un contenedor DI para crearlo para usted. El constructor sin parámetros lo creará con el repositorio predeterminado.
Si sus compañeros de trabajo pueden ver que esto lleva a pruebas mucho más limpias, tal vez puedan darse cuenta de que es una mejor manera. Tal vez puedas hacer que empiecen a codificar así.
Una vez que se hayan dado cuenta de que este es un mejor diseño que el UserController que conoce una implementación específica de UserRepository, puede dar el siguiente paso y mostrarles que puede tener un componente, el contenedor DI, que puede proporcionar automáticamente las instancias que necesita.
fuente
Quizás lo mejor que puede hacer es dar un paso atrás y preguntarse ¿por qué quiere introducir contenedores DI? Si es para mejorar la capacidad de prueba, entonces su primera tarea es lograr que el equipo compre las pruebas unitarias. Personalmente, me preocuparía mucho más la falta de pruebas unitarias que la falta de un contenedor DI.
Armado con un conjunto completo o conjuntos de pruebas unitarias, puede establecer con confianza la evolución de su diseño con el tiempo. Sin ellos, se enfrenta a una lucha cada vez más tensa para mantener su diseño al día con los requisitos cambiantes, una lucha que eventualmente muchos equipos pierden.
Por lo tanto, mi consejo sería probar primero la unidad de campeón y refactorizar, luego, a su vez, introducir DI y finalmente contenedores DI.
fuente
Estoy escuchando algunos grandes no-nos de su equipo aquí:
"No hay bibliotecas de terceros" - AKA "No inventado aquí" o "NIH". El temor es que, al implementar una biblioteca de terceros y hacer que la base de código dependa de ella, si la biblioteca tiene un error, un agujero de seguridad, o incluso una limitación que necesita superar, se vuelve dependiente de este tercero. parte para arreglar su biblioteca para que su código funcione. Mientras tanto, debido a que es "su" programa que falló espectacularmente, o fue pirateado, o no hace lo que le pidieron, aún tiene la responsabilidad (y los desarrolladores de terceros dirán que su herramienta es "tal cual" ", Úselo bajo su propio riesgo).
El contraargumento es que el código que resuelve el problema ya existe en la biblioteca de terceros. ¿Estás construyendo tus computadoras desde cero? ¿Escribiste Visual Studio? ¿Estás evitando las bibliotecas integradas en .NET? No. Usted tiene un nivel de confianza en Intel / AMD y Microsoft, y encuentran errores todo el tiempo (y no siempre los corrigen rápidamente). Agregar una biblioteca de terceros le permite obtener rápidamente una base de código mantenible que funcione, sin tener que reinventar la rueda. Y el ejército de abogados de Microsoft se reirá en tu cara cuando les digas que un error en las bibliotecas de criptografía .NET los hace responsables del incidente de piratería que sufrió tu cliente al usar tu programa .NET. Si bien Microsoft ciertamente saltará para solucionar los agujeros de seguridad de "hora cero" en cualquiera de sus productos,
"Queremos poner todo en un solo ensamblaje", en realidad, no. En .NET al menos, si realiza un cambio en una línea de código, se debe reconstruir todo el ensamblado que contiene esa línea de código. El buen diseño del código se basa en múltiples ensamblajes que puede reconstruir de la manera más independiente posible (o al menos solo tiene que reconstruir dependencias, no dependientes). Si REALMENTE desean lanzar un EXE que sea solo el EXE (que tiene valor en ciertas circunstancias), hay una aplicación para eso; ILMerge.
"DI es tabú" - ¿WTF? DI es una práctica recomendada aceptada en cualquier lenguaje O / O con una construcción de código de "interfaz". ¿Cómo se adhiere su equipo a las metodologías de diseño GRASP o SOLID si no acoplan las dependencias entre objetos? Si no se molestan, entonces están perpetuando la fábrica de espaguetis que hace que el producto sea el complicado pantano que es. Ahora, DI aumenta la complejidad al aumentar el recuento de objetos, y si bien hace que la sustitución de una implementación diferente sea más fácil, hace que cambiar la interfaz sea más difícil (al agregar una capa adicional de objeto de código que tiene que cambiar).
"No necesitamos agregar esta capa de complejidad a un proyecto ya complejo": pueden tener un punto. Una cosa crucial a considerar en cualquier desarrollo de código es YAGNI; "No lo vas a necesitar". Un diseño que está SÓLIDAMENTE diseñado desde el principio es un diseño hecho SÓLIDO "por fe"; no sabe si necesitará la facilidad de cambio que proporcionan los diseños SOLIDOS, pero tiene una corazonada. Si bien puede tener razón, también puede estar muy equivocado; un diseño muy SÓLIDO podría terminar viéndose como "código de lasaña" o "código de ravioles", teniendo tantas capas o trozos pequeños que hacer que los cambios aparentemente triviales se vuelvan difíciles porque tienes que cavar a través de tantas capas y / o trazar una pila de llamadas tan complicada.
Apoyo una regla de tres golpes: haz que funcione, que sea limpio, que sea SÓLIDO. Cuando escribe una línea de código por primera vez, solo tiene que funcionar, y no obtiene puntos por diseños de torre de marfil. Suponga que es algo único y haga lo que tiene que hacer. La segunda vez que mira ese código, probablemente esté buscando expandirlo o reutilizarlo, y no es el único que pensó que sería. En este punto, hazlo más elegante y comprensible refactorizándolo; simplifique la lógica enrevesada, extraiga código repetido en bucles y / o llamadas a métodos, agregue algunos comentarios aquí y allá para cualquier error que tenga que dejar en su lugar, etc. La tercera vez que mire ese código probablemente sea un gran problema. Ahora debe cumplir con las reglas SÓLIDAS; inyecte dependencias en lugar de construirlas, extraiga clases para mantener métodos que sean menos coherentes con la "carne" del código,
"No es como si estuviéramos conectando diferentes implementaciones" - Usted, señor, tiene un equipo que no cree en las pruebas unitarias en sus manos. Creo que es imposible escribir una prueba unitaria, que se ejecuta de forma aislada y no tiene efectos secundarios, sin poder "burlarse" de los objetos que tienen esos efectos secundarios. Cualquier programa no trivial tiene efectos secundarios; si su programa lee o escribe en una base de datos, abre o guarda archivos, se comunica a través de una red o socket periférico, inicia otro programa, dibuja ventanas, salidas a la consola, o usa memoria o recursos fuera del "sandbox" de su proceso, tiene Un efecto secundario. Si no hace ninguna de estas cosas, ¿qué hace y por qué debería comprarlo?
Para ser justos, las pruebas unitarias no son la única forma de demostrar que un programa funciona; puede escribir pruebas de integración, codificar algoritmos probados matemáticamente, etc. Sin embargo, las pruebas unitarias muestran, muy rápidamente, que dadas las entradas A, B y C, este código genera la X esperada (o no), y por lo tanto el código funciona correctamente (o no) en un caso dado. Los conjuntos de pruebas unitarias pueden demostrar con un alto grado de confianza que el código funciona como se espera en todos los casos, y también proporcionan algo que una prueba matemática no puede; una demostración de que el programa TODAVÍA funciona de la manera prevista. Una prueba matemática del algoritmo no prueba que se implementó correctamente, ni tampoco prueba que los cambios realizados se implementaron correctamente y no lo rompieron.
En resumen, si este equipo no ve ningún valor en lo que DI haría, entonces no lo apoyarán, y todos (al menos todos los muchachos mayores) tienen que comprar algo para un cambio real. a suceder Están poniendo mucho énfasis en YAGNI. Estás poniendo mucho énfasis en las pruebas SOLIDAS y automatizadas. En algún lugar en el medio se encuentra la verdad.
fuente
Tenga cuidado al agregar características adicionales (como DI) a un proyecto cuando no esté aprobado. Si tiene un plan de proyecto con límites de tiempo, podría caer en la trampa de no tener tiempo suficiente para terminar las funciones aprobadas.
Incluso si no usa DI ahora, participe en las pruebas para saber exactamente cuáles son las expectativas para las pruebas en su empresa. Habrá otro proyecto y podrá contrastar los problemas con las pruebas del proyecto actual vs. DI.
Si no usa DI, puede ser creativo y hacer que su código DI- "esté listo". Use un constructor sin parámetros para llamar a otros constructores:
fuente
Creo que una de sus mayores preocupaciones es, de hecho, todo lo contrario: DI no está haciendo complejo el proyecto. En la mayoría de los casos está reduciendo mucha complejidad.
Solo piense sin DI, ¿qué va a obtener el objeto / componente dependiente que necesita? Normalmente es mediante la búsqueda desde un repositorio, o obteniendo un singleton, o instanciando usted mismo.
El primero 2 implica un código adicional para localizar el componente dependiente, en el mundo DI, no hizo nada para realizar la búsqueda. La complejidad de sus componentes se reduce de hecho.
Si se está creando una instancia en algunos casos que no es apropiado, incluso está creando más complejidad. Necesita saber cómo crear el objeto correctamente, necesita saber qué valores inicializar el objeto a un estado viable, todo esto crea una complejidad adicional para su código.
Entonces, DI no está haciendo complejo el proyecto, lo está haciendo más simple, al simplificar los componentes.
Otro gran argumento es que no va a conectar una implementación diferente. Sí, es cierto que en el código de producción, no es común tener muchas implementaciones diferentes en el código de producción real. Sin embargo, hay un lugar importante que necesita una implementación diferente: Mock / Stub / Test Doubles en Unit Tests. Para que DI funcione, en la mayoría de los casos se basa en un modelo de desarrollo basado en interfaz, a su vez hace posible la burla / tropezado en pruebas unitarias. Además de reemplazar la implementación, el "diseño orientado a la interfaz" también hace un diseño más limpio y mejor. Por supuesto, aún puede diseñar con interfaz sin DI. Sin embargo, las pruebas unitarias y la DI lo empujan a adoptar tales prácticas.
A menos que sus "dioses" en la compañía piensen que: "código más simple", "pruebas unitarias", "modularización", "responsabilidad única", etc., son algo en contra de ellos, no debería haber ninguna razón para que rechacen el uso de DI.
fuente
"Queremos que sea simple, si es posible, simplemente coloque todo en un solo ensamblaje. DI es una complejidad innecesaria sin beneficio".
El beneficio es que promueve el diseño de interfaces y permite una burla fácil. Lo que en consecuencia facilita las pruebas unitarias. Las pruebas unitarias aseguran un código confiable, modular y fácil de mantener. Si su jefe aún mantiene que es una mala idea, bueno, no hay nada que pueda hacer: es la mejor práctica aceptada en la industria contra la que está discutiendo.
Pídales que intenten escribir una prueba unitaria con un marco DI y una biblioteca burlona, pronto aprenderán a amarla.
fuente
new
a todas partes y escribir clases de fábrica, ya que tiene todos estos componentes en su lugar.¿Trabajas con estas personas en proyectos más pequeños o solo en uno grande? Es más fácil convencer a las personas de que prueben algo nuevo en un proyecto secundario / terciario que el equipo / las empresas. Las razones principales son que si falla, el costo de quitar / reemplazar es mucho menor, y es mucho menos trabajo llevarlo a todas partes en un proyecto pequeño. En uno grande, usted tiene un gran costo inicial para hacer el cambio en todas partes a la vez, o un período prolongado en el que el código es una mezcla del enfoque antiguo / nuevo que agrega fricción porque debe recordar de qué manera está diseñada cada clase trabajar.
fuente
Aunque una de las cosas que promueven la DI es la prueba unitaria, pero creo que agrega una capa de complejidad en algunos casos.
Es mejor tener un automóvil difícil en las pruebas, pero fácil de reparar que un automóvil fácil en las pruebas y difícil en la reparación.
También me parece que el grupo que impulsa la DI solo presenta sus ideas al influir en que algunos enfoques de diseño existentes son malos.
si tenemos un método que realiza 5 funciones diferentes
DI-People dice:
Creo que es mejor crear nuestro propio patrón de diseño que se adapte a las necesidades del negocio en lugar de ser influenciado por DI.
a favor de DI, podría ayudar en algunas situaciones, por ejemplo, si tiene cientos de implementaciones para la misma interfaz depende de un selector y estas implementaciones son muy diferentes en lógica, en este caso iría con DI o DI técnicas similares. pero si tenemos pocas implementaciones para la misma interfaz, no necesitamos DI.
fuente