Entiendo lo que se supone que SOLID debe lograr y lo uso regularmente en situaciones donde la modularidad es importante y sus objetivos son claramente útiles. Sin embargo, dos cosas me impiden aplicarlo consistentemente en mi base de código:
Quiero evitar la abstracción prematura. En mi experiencia, dibujar líneas de abstracción sin casos de uso concretos (del tipo que existe ahora o en el futuro previsible ) lleva a que se dibujen en los lugares equivocados. Cuando intento modificar dicho código, las líneas de abstracción se interponen en lugar de ayudar. Por lo tanto, tiendo a equivocarme al no dibujar líneas de abstracción hasta tener una buena idea de dónde serían útiles.
Me resulta difícil justificar el aumento de la modularidad por sí mismo si hace que mi código sea más detallado, más difícil de entender, etc. y no elimina ninguna duplicación. Encuentro que el código de procedimiento o de objeto de Dios simple y estrechamente acoplado a veces es más fácil de entender que el código de ravioles muy bien factorizado porque el flujo es simple y lineal. También es mucho más fácil escribir.
Por otro lado, esta mentalidad a menudo conduce a objetos de Dios. Generalmente los refactorizo de manera conservadora, agregando líneas de abstracción claras solo cuando veo que surgen patrones claros. ¿Qué hay de malo en los objetos de Dios y el código estrechamente acoplado, si es que no necesita más modularidad, no tiene una duplicación significativa y el código es legible?
EDITAR: En cuanto a los principios SÓLIDOS individuales, quise enfatizar que la sustitución de Liskov es en mi humilde opinión una formalización del sentido común y debe aplicarse en todas partes, ya que las abstracciones no tienen sentido si no lo es. Además, cada clase debe tener una responsabilidad única en algún nivel de abstracción, aunque puede ser un nivel muy alto con los detalles de implementación agrupados en una gran clase de 2,000 líneas. Básicamente, tus abstracciones deberían tener sentido donde elijas abstraer. Los principios que cuestiono en los casos en los que la modularidad no es claramente útil son los abiertos y cerrados, la segregación de la interfaz y especialmente la inversión de dependencia, ya que se trata de la modularidad, no solo de que las abstracciones tengan sentido.
fuente
Respuestas:
Los siguientes son principios simples que puede aplicar para ayudarlo a comprender cómo equilibrar el diseño de su sistema:
S
en SÓLIDO. Puede tener un objeto muy grande en términos de número de métodos o la cantidad de datos y aún mantener este principio. Tomemos por ejemplo el objeto Ruby String. Este objeto tiene más métodos de los que puede sacudir un palo, pero todavía tiene una sola responsabilidad: mantener una cadena de texto para la aplicación. Tan pronto como su objeto comience a asumir una nueva responsabilidad, comience a pensar mucho en eso. El problema de mantenimiento es preguntarse "¿dónde esperaría encontrar este código si tuviera problemas con él más adelante?"En esencia, está tratando de hacer lo que puede para no dispararse en el pie cuando llega el momento de mantener el software. Si sus objetos grandes son abstracciones razonables, entonces hay pocas razones para dividirlos solo porque a alguien se le ocurrió una métrica que dice que una clase no debe ser más que X líneas / métodos / propiedades / etc. En todo caso, esas son pautas y no reglas duras y rápidas.
fuente
Creo que en su mayor parte has respondido tu propia pregunta. SOLID es un conjunto de pautas sobre cómo refactorizar su código, cuando los conceptos deben subir niveles de abstracciones.
Como todas las otras disciplinas creativas, no hay absolutos, solo compensaciones. Cuanto más lo haga, "más fácil" será decidir cuándo es suficiente para el dominio o los requisitos del problema actual.
Una vez dicho esto, la abstracción es el corazón del desarrollo de software, por lo que si no hay una buena razón para no hacerlo, la práctica lo hará mejor y obtendrá una idea intuitiva de las compensaciones. Así que favorezca a menos que se vuelva perjudicial.
fuente
Esto es parcialmente correcto y parcialmente incorrecto.
Incorrecto
Esta no es una razón para evitar que uno aplique los principios OO / SÓLIDO. Solo evita aplicarlos demasiado temprano.
Cual es...
Derecha
No refactorice el código hasta que sea refactible; cuando se trata de requisitos completos. O, cuando los "casos de uso" están todos allí como usted dice.
Cuando se trabaja solo o en un silo
El problema de no hacer OO no es inmediatamente obvio. Después de escribir la primera versión de un programa:
3/4 de estas cosas buenas mueren rápidamente en poco tiempo.
Finalmente, reconoce que tiene objetos de Dios (objetos con muchas funciones), por lo que reconoce funciones individuales. Encapsular. Póngalos en carpetas. Use interfaces de vez en cuando. Hazte un favor para el mantenimiento a largo plazo. Especialmente porque los métodos no refactorizados tienden a hincharse infinitamente y convertirse en métodos de Dios.
En un equipo
Los problemas de no hacer OO se notan de inmediato. Un buen código OO es al menos algo autodocumentable y legible. Especialmente cuando se combina con un buen código verde. Además, la falta de estructura dificulta la separación de tareas y la integración es mucho más compleja.
fuente
No hay nada malo con los objetos grandes y el código estrechamente acoplado cuando son apropiados y cuando no se requiere nada mejor diseñado . Esta es solo otra manifestación de una regla general que se convierte en dogma.
El acoplamiento flojo y los objetos pequeños y simples tienden a proporcionar beneficios específicos en muchos casos comunes, por lo que es una buena idea usarlos, en general. El problema radica en que las personas que no entienden la lógica detrás de los principios tratan ciegamente de aplicarlos universalmente, incluso cuando no se aplican.
fuente
Tiendo a abordar esto más desde el punto de vista de "No lo vas a necesitar". Esta publicación se centrará en un elemento en particular: la herencia.
En mi diseño inicial creo una jerarquía de cosas que sé que habrá dos o más. Es probable que necesiten mucho del mismo código, por lo que vale la pena diseñarlo desde el principio. Después de que el código inicial está en su lugar , y necesito agregar más características / funcionalidad, miro lo que tengo y pienso: "¿Hay algo que ya haya implementado la misma función o similar?" Si es así, es muy probable que se trate de una nueva abstracción que pide ser lanzada.
Un buen ejemplo de esto es un marco MVC. Comenzando desde cero, creas una página grande con un código detrás. Pero luego quieres agregar otra página. Sin embargo, el código subyacente ya implementa mucha de la lógica que requiere la nueva página. Por lo tanto, abstrae una
Controller
clase / interfaz que implementará la lógica específica de esa página, dejando atrás las cosas comunes en el código original "dios".fuente
Si USTED sabe como desarrollador cuándo NO aplicar los principios debido al futuro del código, entonces es bueno para usted.
Espero que SOLID permanezca en su mente para saber cuándo deben abstraerse las cosas para evitar la complejidad y mejorar la calidad del código si comienza a degradarse en los valores que usted indicó.
Sin embargo, lo que es más importante, considere que la mayoría de los desarrolladores están haciendo el trabajo diario y no les importa tanto como a usted. Si inicialmente estableció métodos de ejecución prolongada, ¿qué pensarán los otros desarrolladores que entran en el código cuando lo mantengan o lo extiendan? ... Sí, lo adivinó, funciones MÁS GRANDES, clases de ejecución MÁS LARGAS y MÁS Dios se opone, y no tienen los principios en mente para poder captar el código creciente y encapsularlo correctamente. Su código "legible" ahora se convierte en un desastre podrido.
Por lo tanto, puede argumentar que un mal desarrollador lo hará independientemente, pero al menos tiene una mejor ventaja si las cosas se mantienen simples y bien organizadas desde el principio.
No me importa particularmente un "Mapa del Registro" global o un "Objeto de Dios" si son simples. Realmente depende del diseño del sistema, a veces puede salirse con la suya y seguir siendo simple, a veces no puede.
Tampoco olvidemos que las funciones grandes y los Objetos de Dios pueden resultar extremadamente difíciles de probar. Si desea permanecer ágil y sentirse "seguro" al volver a factorizar y cambiar el código, necesita pruebas. Las pruebas son difíciles de escribir cuando las funciones u objetos hacen muchas cosas.
fuente
El problema con un objeto dios es que generalmente puedes dividirlo en pedazos de manera rentable. Por definición, hace más de una cosa. Todo el propósito de dividirlo en clases más pequeñas es para que pueda tener una clase que haga una cosa bien (y tampoco debe ampliar la definición de "una cosa"). Esto significa que, para cualquier clase, una vez que sepas lo que se supone que debe hacer, deberías poder leerlo con bastante facilidad y decir si está haciendo eso correctamente o no.
creo que ahi es algo así como tener demasiado modularidad, y un exceso de flexibilidad, pero eso es más de un problema de sobre-diseño y el exceso de ingeniería, donde le contabilización de los requisitos que ni siquiera saben que el cliente desea. Muchas ideas de diseño están orientadas a facilitar el control de los cambios en la forma en que se ejecuta el código, pero si nadie espera el cambio, entonces no tiene sentido incluir la flexibilidad.
fuente
Queryer
que optimiza las consultas de la base de datos, consulta el RDBMS y analiza los resultados en objetos para devolver. Si solo hay una manera de hacer esto en su aplicación yQueryer
está sellado herméticamente y encapsula de esta manera, entonces solo hace una cosa. Si hay varias formas de hacer esto y alguien podría preocuparse por los detalles de una parte, entonces hace varias cosas y es posible que desee dividirlo.Utilizo una directriz más simple: si puede escribir pruebas unitarias para ello y no tiene duplicación de código, es lo suficientemente abstracto. Además, estás en una buena posición para una refactorización posterior.
Más allá de eso, debe tener SOLID en mente, pero más como una guía que como una regla.
fuente
Si la aplicación es lo suficientemente pequeña, cualquier cosa es mantenible. En aplicaciones más grandes, los objetos de Dios se convierten rápidamente en un problema. Eventualmente, implementar una nueva característica requiere modificar 17 objetos de Dios. A las personas no les va muy bien siguiendo los procedimientos de 17 pasos. Los objetos de Dios están siendo modificados constantemente por múltiples desarrolladores, y esos cambios tienen que fusionarse repetidamente. No quieres ir allí.
fuente
Comparto sus preocupaciones sobre la abstracción excesiva e inapropiada, pero no estoy necesariamente tan preocupado por la abstracción prematura.
Probablemente eso suene como una contradicción, pero es poco probable que el uso de una abstracción cause un problema siempre y cuando no se comprometa demasiado pronto, es decir, siempre que esté dispuesto y pueda refactorizar si es necesario.
Lo que esto implica es cierto grado de creación de prototipos, experimentación y retroceso en el código.
Dicho esto, no hay una regla determinista simple a seguir que resuelva todos los problemas. La experiencia cuenta mucho, pero tienes que ganar esa experiencia cometiendo errores. Y siempre hay más errores para cometer que los errores anteriores no te habrán preparado.
Aun así, trate los principios de los libros de texto como un punto de partida, luego aprenda a programar mucho y vea cómo funcionan esos principios. Si fuera posible dar pautas mejores, más precisas y confiables que cosas como SOLID, alguien probablemente lo habría hecho ahora, e incluso si lo hubieran hecho, esos principios mejorados seguirían siendo imperfectos, y la gente estaría preguntando sobre las limitaciones en esos .
Buen trabajo también: si alguien pudiera proporcionar un algoritmo claro y determinista para diseñar y codificar programas, solo habría un último programa para que los humanos lo escriban: el programa que escribiría automáticamente todos los programas futuros sin intervención humana.
fuente
Dibujar las líneas de abstracción apropiadas es algo que uno extrae de la experiencia. Cometerá errores al hacerlo, lo que es innegable.
SOLID es una forma concentrada de esa experiencia que le brindan las personas que obtuvieron esta experiencia de la manera difícil. Casi lo has clavado cuando dices que te abstendrás de crear abstracciones antes de que veas la necesidad de hacerlo. Bueno, la experiencia que SOLID le brinda lo ayudará a resolver problemas que nunca existió . Pero confía en mí ... hay muy reales.
SOLID se trata de modularidad, la modularidad se trata de mantenibilidad, y la mantenibilidad se trata de permitirle mantener un horario de trabajo humano una vez que el software alcanza la producción y el cliente comienza a notar errores que usted no tiene.
El mayor beneficio de la modularidad es la capacidad de prueba. Cuanto más modular sea su sistema, más fácil será probarlo y más rápido podrá construir bases sólidas para continuar con los aspectos más difíciles de su problema. Por lo tanto, es posible que su problema no exija esta modularidad, pero el hecho de que le permita crear un mejor código de prueba más rápido es una obviedad para luchar por la modularidad.
Ser ágil se trata de intentar lograr el delicado equilibrio entre el envío rápido y proporcionar una buena calidad. Ágil no significa que debamos cortar el camino, de hecho, los proyectos ágiles más exitosos en los que he estado involucrado son aquellos que prestaron mucha atención a tales pautas.
fuente
Un punto importante que parece haberse pasado por alto es que SOLID se practica junto con TDD. TDD tiende a resaltar lo que es "apropiado" en su contexto particular y ayuda a aliviar muchas de las equivocaciones que parecen estar en el consejo.
fuente