Me gusta SOLID, y hago mi mejor esfuerzo para usarlo y aplicarlo cuando estoy desarrollando. Pero no puedo evitar sentir que el enfoque SOLID convierte su código en código 'marco', es decir, código que diseñaría si estuviera creando un marco o biblioteca para que otros desarrolladores lo utilicen.
En general, he practicado 2 modos de programación: crear más o menos exactamente lo que se solicita a través de requisitos y KISS (programación típica), o crear lógica, servicios, etc. genéricos y reutilizables que brindan la flexibilidad que otros desarrolladores pueden necesitar (programación marco) .
Si el usuario realmente solo quiere que una aplicación haga cosas xey, ¿tiene sentido seguir SOLID y agregar un montón de puntos de entrada de abstracción, cuando ni siquiera sabe si ese es un problema válido para comenzar? ¿con? Si agrega estos puntos de entrada de abstracción, ¿realmente está cumpliendo con los requisitos de los usuarios, o está creando un marco sobre su marco existente y la pila de tecnología para facilitar futuras adiciones? ¿En qué caso atiende los intereses del cliente o del desarrollador?
Esto es algo que parece común en el mundo de Java Enterprise, donde se siente como si estuvieras diseñando tu propio framework sobre J2EE o Spring para que sea un mejor UX para el desarrollador, en lugar de enfocarte en UX para el usuario.
fuente
Respuestas:
Su observación es correcta, los principios SOLID están hechos en mi humilde opinión teniendo en cuenta bibliotecas reutilizables o código de marco. Cuando solo los sigue a ciegas, sin preguntar si tiene sentido o no, se arriesga a generalizar en exceso e invertir mucho más esfuerzo en su sistema de lo que probablemente sea necesario.
Esto es una compensación, y se necesita algo de experiencia para tomar las decisiones correctas sobre cuándo generalizar y cuándo no. Un posible enfoque para esto es apegarse al principio de YAGNI: no haga que su código sea SÓLIDO "por si acaso" o, para usar sus palabras: no
en su lugar, brinde la flexibilidad que otros desarrolladores realmente necesitan tan pronto como la necesiten , pero no antes.
Entonces, siempre que tenga una función o clase en su código, no está seguro de si podría reutilizarse, no lo ponga en su marco en este momento. Espere hasta que tenga un caso real de reutilización y refactorice a "Suficientemente sólido para ese caso". No implemente más capacidad de configuración (siguiendo el OCP), o puntos de entrada de abstracción (usando el DIP) en una clase tal como realmente necesita para el caso de reutilización real. Agregue la siguiente flexibilidad cuando el siguiente requisito para la reutilización esté realmente allí.
Por supuesto, esta forma de trabajar siempre requerirá cierta cantidad de refactorización en la base de código de trabajo existente. Es por eso que las pruebas automáticas son importantes aquí. Por lo tanto, hacer que su código sea lo suficientemente SÓLIDO desde el principio para que se pueda probar la unidad no es una pérdida de tiempo, y hacerlo no contradice a YAGNI. Las pruebas automáticas son un caso válido para la "reutilización del código", ya que el código en juego se usa tanto del código de producción como de las pruebas. Pero tenga en cuenta, solo agregue la flexibilidad que realmente necesita para que las pruebas funcionen, nada menos y nada más.
Esto es en realidad vieja sabiduría. Hace mucho tiempo, antes de que el término SÓLIDO se popularizara, alguien me dijo que antes de intentar escribir código reutilizable , deberíamos escribir código utilizable . Y sigo pensando que esta es una buena recomendación.
fuente
Según mi experiencia, al escribir una aplicación, tienes tres opciones:
En el primer caso, es común terminar con un código estrechamente acoplado que carece de pruebas unitarias. Claro que es rápido de escribir, pero es difícil de probar. Y es un verdadero dolor real cambiar más tarde cuando cambian los requisitos.
En el segundo caso, se gasta una gran cantidad de tiempo tratando de anticipar las necesidades futuras. Y con demasiada frecuencia esos requisitos futuros anticipados nunca se materializan. Este parece el escenario que estás describiendo. Es una pérdida de esfuerzo la mayor parte del tiempo y da como resultado un código innecesariamente complejo que todavía es difícil de cambiar cuando aparece un requisito que no se había previsto.
El último caso es el que apunto en mi opinión. Use TDD o técnicas similares para probar el código a medida que avanza y terminará con un código débilmente acoplado, que es fácil de modificar pero rápido de escribir. Y la cuestión es que, al hacer esto, usted naturalmente sigue muchos de los principios SÓLIDOS: clases y funciones pequeñas; interfaces y dependencias inyectadas. Y la Sra. Liskov también se mantiene contenta en general, ya que las clases simples con responsabilidades únicas rara vez incumplen su principio de sustitución.
El único aspecto de SOLID que realmente no se aplica aquí es el principio abierto / cerrado. Para bibliotecas y marcos, esto es importante. Para una aplicación autónoma, no tanto. Realmente se trata de escribir código que sigue a " SLID ": fácil de escribir (y leer), fácil de probar y fácil de mantener.
fuente
La perspectiva que tiene puede verse sesgada por la experiencia personal. Esta es una pendiente resbaladiza de hechos que son correctos individualmente, pero la inferencia resultante no lo es, a pesar de que parece correcto a primera vista.
Esto significa que cuando interactúa con marcos y bibliotecas más pequeñas, el código de buenas prácticas con el que interactuará se encontrará más comúnmente en los marcos más grandes.
Esta falacia es muy común, por ejemplo, todos los médicos que me han tratado fueron arrogantes. Por lo tanto, concluyo que todos los médicos son arrogantes. Estas falacias siempre sufren de hacer una inferencia general basada en experiencias personales.
En su caso, es posible que haya experimentado predominantemente buenas prácticas en marcos más grandes y no en bibliotecas más pequeñas. Su observación personal no es incorrecta, pero es evidencia anecdótica y no aplicable universalmente.
Estás confirmando esto aquí. Piensa en qué es un marco. No es una aplicación. Es una "plantilla" generalizada que otros pueden usar para hacer todo tipo de aplicaciones. Lógicamente, eso significa que un marco está construido en una lógica mucho más abstracta para que todos puedan usarlo.
Los creadores de framework no pueden tomar atajos porque ni siquiera saben cuáles son los requisitos de las aplicaciones posteriores. Construir un marco de trabajo los incentiva inherentemente a hacer que su código sea utilizable para otros.
Sin embargo, los creadores de aplicaciones tienen la capacidad de comprometer la eficiencia lógica porque están enfocados en entregar un producto. Su objetivo principal no es el funcionamiento del código sino la experiencia del usuario.
Para un marco, el usuario final es otro desarrollador, que interactuará con su código. La calidad de su código es importante para su usuario final.
Para una aplicación, el usuario final no es desarrollador y no interactuará con su código. La calidad de su código no es importante para ellos.
Esto es exactamente por qué los arquitectos de un equipo de desarrollo a menudo actúan como ejecutores de buenas prácticas. Están a un paso de entregar el producto, lo que significa que tienden a mirar el código de manera objetiva, en lugar de enfocarse en la entrega de la aplicación misma.
Este es un punto interesante, y es (en mi experiencia) la razón principal por la cual las personas todavía tratan de justificar evitar las buenas prácticas.
Para resumir los siguientes puntos: omitir las buenas prácticas solo puede justificarse si sus requisitos (como se conocen actualmente) son inmutables, y nunca habrá ningún cambio / adición a la base de código. Alerta de spoiler: rara vez es así.
Por ejemplo, cuando escribo una aplicación de consola de 5 minutos para procesar un archivo en particular, no utilizo las buenas prácticas. Debido a que solo voy a usar la aplicación hoy, y no necesita actualizarse en el futuro (sería más fácil escribir una aplicación diferente en caso de que necesite otra).
Supongamos que puede compilar una aplicación de manera inadecuada en 4 semanas, y puede compilarla adecuadamente en 6 semanas. A primera vista, de mala calidad construirlo parece mejor. El cliente obtiene su solicitud más rápido y la empresa tiene que dedicar menos tiempo a los salarios de los desarrolladores. Ganar / ganar, ¿verdad?
Sin embargo, esta es una decisión tomada sin pensar en el futuro. Debido a la calidad de la base de código, realizar un cambio importante en el construido de manera inadecuada tomará 2 semanas, mientras que realizar los mismos cambios en el construido correctamente lleva 1 semana. Puede haber muchos de estos cambios en el futuro.
Además, hay una tendencia a que los cambios requieran inesperadamente más trabajo del que inicialmente pensabas en bases de código construidas de forma inadecuada, lo que probablemente aumenta el tiempo de desarrollo a tres semanas en lugar de dos.
Y luego también está la tendencia a perder el tiempo buscando insectos. Este suele ser el caso en los proyectos en los que se ha ignorado el registro debido a limitaciones de tiempo o la falta de voluntad para implementarlo porque distraídamente trabajas asumiendo que el producto final funcionará como se esperaba.
Ni siquiera necesita ser una actualización importante. En mi empleador actual, he visto varios proyectos que se construyeron de manera rápida y sucia, y cuando el más pequeño error / cambio tuvo que realizarse debido a una falta de comunicación en los requisitos, eso condujo a una reacción en cadena de la necesidad de refactorizar módulo tras módulo . Algunos de estos proyectos terminaron colapsando (y dejando un desastre imposible de mantener) incluso antes de que lanzaran su primera versión.
Las decisiones de acceso directo (programación rápida y sucia) solo son beneficiosas si puede garantizar de manera concluyente que los requisitos son exactamente correctos y que nunca será necesario cambiarlos. En mi experiencia, nunca me he encontrado con un proyecto donde eso sea cierto.
Invertir el tiempo extra en buenas prácticas es invertir en el futuro. Los errores y cambios futuros serán mucho más fáciles cuando la base de código existente se base en buenas prácticas. Ya estará pagando dividendos después de que solo se realicen dos o tres cambios.
fuente
¿Cómo convierte SOLID el código simple en código marco? No soy un stan para SOLID de ninguna manera, pero realmente no es obvio lo que quieres decir aquí.
Admito que yo mismo no pienso en términos SÓLIDOS, porque llegué a través de las escuelas de programación Gang of Four y Josh Bloch , no de la escuela Bob Martin. Pero realmente creo que si piensas que “SOLIDO” = “agregar más capas a la pila tecnológica”, lo estás leyendo mal.
PD: No venda los beneficios de "mejor experiencia de usuario para el desarrollador" corto. Code pasa la mayor parte de su vida en mantenimiento. Un desarrollador eres tú .
fuente
class A{ int X; int Y; } class A_setX{ f(A a, int N) { a.X = N; }} class A_getX{ int f(A a) { return X; }} class A_setY ... etc.
Creo que lo estás viendo desde un punto de vista demasiado meta con tu reclamo de fábrica. La inicialización no es un aspecto del problema del dominio.