Entonces, mirando alrededor, noté algunos comentarios sobre los métodos largos como una mala práctica.
No estoy seguro de estar siempre de acuerdo en que los métodos largos son malos (y me gustaría recibir opiniones de otros).
Por ejemplo, tengo algunas vistas de Django que procesan un poco los objetos antes de enviarlos a la vista, un método largo de 350 líneas de código. Tengo mi código escrito para que se ocupe de los parámetros: ordenar / filtrar el conjunto de consultas, luego, poco a poco, se procesa un poco en los objetos que mi consulta ha devuelto.
Entonces, el procesamiento es principalmente una agregación condicional, que tiene reglas suficientemente complejas que no se pueden hacer fácilmente en la base de datos, por lo que tengo algunas variables declaradas fuera del bucle principal y luego se modifican durante el bucle.
variable_1 = 0
variable_2 = 0
for object in queryset :
if object.condition_condition_a and variable_2 > 0 :
variable 1+= 1
.....
...
.
more conditions to alter the variables
return queryset, and context
Entonces, de acuerdo con la teoría, debo factorizar todo el código en métodos más pequeños, de modo que tenga el método de visualización como máximo de una página.
Sin embargo, después de haber trabajado en varias bases de código en el pasado, a veces encuentro que hace que el código sea menos legible, cuando necesitas saltar constantemente de un método a otro para descubrir todas las partes, mientras mantienes el método más externo en tu cabeza.
Encuentro que con un método largo que está bien formateado, puede ver la lógica más fácilmente, ya que no se oculta en los métodos internos.
Podría factorizar el código en métodos más pequeños, pero a menudo se usa un bucle interno para dos o tres cosas, por lo que daría como resultado un código más complejo o métodos que no hacen una cosa sino dos o tres (alternativamente Podría repetir bucles internos para cada tarea, pero luego habrá un golpe de rendimiento).
Entonces, ¿hay algún caso en que los métodos largos no siempre sean malos? ¿Siempre hay un caso para los métodos de escritura, cuando solo se usarán en un lugar?
ACTUALIZACIÓN: Parece que hice esta pregunta hace más de un año.
Entonces refactoré el código después de la respuesta (mixta) aquí, lo dividí en métodos. Es una aplicación de Django que recupera conjuntos complejos de objetos relacionados de la base de datos, por lo que el argumento de la prueba está fuera (probablemente habría llevado la mayor parte del año crear objetos relevantes para los casos de prueba. Tengo un tipo de "esto se hizo ayer" ambiente de trabajo antes de que nadie se queje). Corregir errores en esa parte del código es marginalmente más fácil ahora, pero no de manera masiva.
antes de :
#comment 1
bit of (uncomplicated) code 1a
bit of code 2a
#comment 2
bit of code 2a
bit of code 2b
bit of code 2c
#comment 3
bit of code 3
ahora:
method_call_1
method_call_2
method_call_3
def method_1
bit of (uncomplicated) code 1a
bit of code 2a
def method_2
bit of code 2a
bit of code 2b
bit of code 2c
def method_3
bit of code 3
fuente
Respuestas:
No, los métodos largos no siempre son malos.
En el libro Code Complete , se mide que los métodos largos a veces son más rápidos y fáciles de escribir, y no conducen a problemas de mantenimiento.
De hecho, lo que es realmente importante es mantenerse SECO y respetar la separación de las preocupaciones. En algún momento, el cálculo es largo para escribir, pero realmente no causará problemas en el futuro.
Sin embargo, desde mi experiencia personal, la mayoría de los métodos largos tienden a carecer de separación de preocupaciones. De hecho, los métodos largos son una forma fácil de detectar que algo PUEDE estar mal en el código, y que se requiere un cuidado especial aquí al hacer la revisión del código.
EDITAR: A medida que se hacen comentarios, agrego un punto interesante a la respuesta. De hecho, también verificaría las métricas de complejidad para la función (NPATH, complejidad ciclomática o incluso mejor CRAP).
De hecho, recomiendo no verificar tales métricas en funciones largas, sino incluir alertas en ellas con herramientas automatizadas (como checkstyle para java, por ejemplo) EN CADA FUNCIÓN.
fuente
La mayor parte del enfoque aquí parece estar siempre alrededor de la palabra . Sí, los absolutos son malos, y la ingeniería de software es casi tanto arte como ciencia, y todo eso ... pero voy a tener que decir que para el ejemplo que diste, el método sería mejor si se dividiera arriba. Estos son los argumentos que normalmente usaría para justificar la división de su método:
Legibilidad: no estoy seguro acerca de los demás, pero no puedo leer 350 líneas de código rápidamente. Sí, si es mi propio código, y puedo hacer muchas suposiciones al respecto, podría leerlo muy rápidamente, pero eso no viene al caso. Considere cuánto más fácil sería leer ese método, si consistiera en 10 llamadas a otros métodos (cada uno con un nombre descriptivo). Al hacer esto, ha introducido una estratificación en el código, y el método de alto nivel le da al lector un breve resumen con respecto a lo que está sucediendo.
Editar: para expresarlo de otra manera, piense que es así: ¿cómo explicaría ese método a un nuevo miembro del equipo? Seguramente tiene alguna estructura que puede resumir en las líneas de "bueno, comienza haciendo A, luego B, luego a veces C, etc.". Tener un breve método de "descripción general" que llama a otros métodos hace que esta estructura sea obvia. Es extremadamente raro encontrar 350 líneas de código que no se benefician; el cerebro humano no está destinado a lidiar con listas de cientos de elementos, los agrupamos.
Reutilización: los métodos largos tienden a tener baja cohesión; a menudo hacen más de una cosa. La baja cohesión es el enemigo de la reutilización; Si combina múltiples tareas en un método, terminará siendo reutilizado en menos lugares de lo que debería haber sido.
Capacidad de prueba y cohesión: mencioné la complejidad ciclomática en un comentario anterior: es una medida bastante buena de cuán complejo es su método. Representa el límite inferior del número de rutas únicas a través de su código, dependiendo de las entradas (edición: corregida según el comentario de MichaelT). También significa que para probar adecuadamente su método, necesitaría tener al menos tantos casos de prueba como su número de complejidad ciclomática. Desafortunadamente, cuando juntas partes de código que realmente no dependen entre sí, no hay forma de estar seguro de esta falta de dependencia, y la complejidad tiende a multiplicarse. Puede pensar en esta medida como una indicación de la cantidad de cosas diferentes que está tratando de hacer. Si es demasiado alto, es hora de dividir y conquistar.
Refactorización y estructura: los métodos largos a menudo son signos de que falta una estructura en el código. A menudo, el desarrollador no pudo determinar cuáles son los puntos en común entre las diferentes partes de ese método, y dónde podría trazarse una línea entre ellas. Darse cuenta de que un método largo es un problema y tratar de dividirlo en métodos más pequeños es el primer paso en un camino más largo para identificar realmente una mejor estructura para todo. Quizás necesite crear una clase o dos; ¡no será necesariamente más complejo al final!
También creo que en este caso, la excusa para tener un método largo es "... algunas variables declaradas fuera del ciclo principal se alteran durante el ciclo". No soy un experto en Python, pero estoy bastante seguro de que este problema podría solucionarse trivialmente mediante alguna forma de pasar por referencia.
fuente
someVar.toString()
y sentido? ¿Necesitabas ver el código de toString para saber qué está haciendo? Acabas de leer más allá debido a la buena denominación de métodos.Los métodos largos siempre son malos, pero ocasionalmente son mejores que las alternativas.
fuente
Los métodos largos son un código de olor . Por lo general, indican que algo anda mal, pero no es una regla estricta. Por lo general, los casos en los que están justificados implican muchas reglas comerciales estatales y bastante complejas (como ha encontrado).
En cuanto a su otra pregunta, con frecuencia es útil separar fragmentos de lógica en métodos separados, incluso si solo se llaman una vez. Hace que sea más fácil ver la lógica de alto nivel y puede hacer que el manejo de excepciones sea un poco más limpio. ¡Siempre y cuando no tenga que pasar veinte parámetros para representar el estado de procesamiento!
fuente
Los métodos largos no siempre son malos. Por lo general, son una señal de que puede haber un problema.
En el sistema en el que estoy trabajando tenemos media docena de métodos que tienen más de 10,000 líneas de largo. Uno tiene actualmente 54.830 líneas de largo. Y esta bien.
Estas funciones ridículamente largas son muy simples y se autogeneran. Ese gran monstruo de 54.830 líneas de largo contiene los datos diarios de movimiento polar del 1 de enero de 1962 al 10 de enero de 2012 (nuestro último lanzamiento). También lanzamos un procedimiento mediante el cual nuestros usuarios pueden actualizar ese archivo autogenerado. Ese archivo fuente contiene los datos de movimiento polar de http://data.iers.org/products/214/14443/orig/eopc04_08_IAU2000.62-now , traducido automáticamente a C ++.
Leer ese sitio web sobre la marcha no es posible en una instalación segura. No hay conexión con el mundo exterior. La descarga del sitio web como una copia local y el análisis en C ++ tampoco es una opción; el análisis es lento , y esto tiene que ser rápido. Descarga, traducción automática a C ++ y compilación: ahora tiene algo que es rápido. (Simplemente no lo compile optimizado. Es sorprendente cuánto tiempo tarda un compilador optimizador en compilar 50,000 líneas de código de línea recta extremadamente simple. Mi computadora tarda más de media hora en compilar ese archivo optimizado. Y la optimización no logra absolutamente nada No hay nada que optimizar. Es un simple código de línea recta, una declaración de asignación tras otra.)
fuente
Digamos que hay formas buenas y malas de romper un método largo. Tener que "[mantener] el método más externo en tu cabeza" es una señal de que no lo estás dividiendo de la manera más óptima, o que tus submétodos están mal nombrados. En teoría, hay casos en los que un método largo es mejor. En la práctica, es extremadamente raro. Si no puede encontrar la manera de hacer que un método más corto sea legible, pídale a alguien que revise su código y pídales específicamente ideas para acortar los métodos.
En cuanto a los múltiples bucles que causan un supuesto impacto en el rendimiento, no hay forma de saberlo sin medir. Múltiples bucles más pequeños pueden ser significativamente más rápidos si significa que todo lo que necesita puede permanecer en caché. Incluso si hay un impacto en el rendimiento, generalmente es insignificante a favor de la legibilidad.
Diré que a menudo los métodos largos son más fáciles de escribir , aunque son más difíciles de leer . Por eso proliferan aunque a nadie le gusten. No hay nada de malo en planificar desde el principio para refactorizar antes de registrarlo.
fuente
Los métodos largos pueden ser más eficientes desde el punto de vista computacional y espacial, puede ser más fácil ver la lógica y más fácil depurarlos. Sin embargo, estas reglas solo se aplican cuando solo un programador toca ese código. El código será difícil de extender si no es atómico, esencialmente la siguiente persona tendrá que comenzar desde cero y luego depurar y probar esto tomará para siempre, ya que no está utilizando ningún código bueno conocido.
fuente
Hay algo que llamamos descomposición funcional que implica dividir sus métodos más largos en métodos más pequeños siempre que sea posible. Como mencionó que su método implica ordenar / filtrar, es mejor que tenga métodos o funciones separados para estas tareas.
Precisamente, su método debe centrarse en realizar solo 1 tarea.
Y si necesita llamar a otro método por algún motivo, continúe con el método que ya está escribiendo. También para fines de legibilidad, puede agregar comentarios. Convencionalmente, los programadores usan comentarios de varias líneas (/ ** / en C, C ++, C # y Java) para las descripciones de los métodos y usan comentarios de una sola línea (// en C, C ++, C # y Java). También hay buenas herramientas de documentación disponibles para una mayor legibilidad del código (por ejemplo, JavaDoc ). También puede buscar comentarios basados en XML si es un desarrollador de .Net.
Los bucles impactan el rendimiento del programa y pueden causar sobrecarga de la aplicación si no se usan correctamente. La idea es diseñar su algoritmo de tal manera que use bucles anidados lo menos posible.
fuente
Está perfectamente bien escribir funciones largas. Pero varía según el contexto si realmente lo necesita o no. Por ejemplo, algunos de los mejores algoritmos se expresan mejor cuando es una pieza. Por otro lado, un gran porcentaje de rutinas en programas orientados a objetos serán rutinas accesorias, que serán muy cortas. Algunas de las largas rutinas de procesamiento que tienen largas cajas de interruptores, si las condiciones pueden optimizarse a través de métodos controlados por tablas.
Hay una excelente discusión breve en Code Complete 2 sobre la duración de las rutinas.
fuente
Otra votación que casi siempre está mal. Sin embargo, encuentro dos casos básicos en los que es la respuesta correcta:
1) Un método que básicamente solo llama a un montón de otros métodos y no funciona realmente en sí mismo. Tiene un proceso que lleva 50 pasos, se obtiene un método con 50 llamadas. Por lo general, no se gana nada al tratar de romper eso.
2) Despachadores. El diseño OOP ha eliminado la mayoría de estos métodos, pero las fuentes de datos entrantes son, por su propia naturaleza, solo datos y, por lo tanto, no pueden seguir los principios de OOP. No es exactamente inusual tener algún tipo de rutina de despachador en el código que maneja los datos.
También diría que uno ni siquiera debería considerar la pregunta cuando se trata de cosas autogeneradas. Nadie está tratando de entender lo que hace el código autogenerado, no importa si es fácil de entender para un humano.
fuente
Quería abordar el ejemplo que diste:
En mi empresa, nuestro proyecto más grande se basa en Django y también tenemos funciones de visión prolongada (muchas son más de 350 líneas). Yo diría que los nuestros no necesitan ser tan largos, y nos están haciendo daño.
Estas funciones de vista están haciendo un montón de trabajo vagamente relacionado que se debe extraer al modelo, las clases auxiliares o las funciones auxiliares. Además, terminamos reutilizando las vistas para hacer cosas diferentes, que en su lugar deberían dividirse en vistas más coherentes.
Sospecho que sus puntos de vista tienen características similares. En mi caso, sé que causa problemas y estoy trabajando para hacer cambios. Si no está de acuerdo con que está causando problemas, entonces no necesita arreglarlo.
fuente
No sé si alguien ya ha mencionado esto, pero una de las razones por las cuales los métodos largos son malos es porque generalmente involucran varios niveles diferentes de abstracción. Tienes variables de bucle y todo tipo de cosas que suceden. Considere la función ficticia:
Si estuviera haciendo toda la animación, cálculo, acceso a datos, etc. en esa función, habría sido un desastre. La función nextSlide () mantiene una capa de abstracción consistente (el sistema de estado de la diapositiva) e ignora otras. Esto hace que el código sea legible.
Si tiene que ingresar constantemente a métodos más pequeños para ver lo que hacen, entonces el ejercicio de dividir la función ha fallado. El hecho de que el código que está leyendo no esté haciendo cosas obvias en los métodos secundarios no significa que los métodos secundarios sean una mala idea, solo que se hizo incorrectamente.
Cuando creo métodos, generalmente termino dividiéndolos en métodos más pequeños como una especie de estrategia de dividir y conquistar. Un método como
es ciertamente más legible que
¿Derecho?
Estoy de acuerdo en que las declaraciones absolutas son malas y también estoy de acuerdo en que generalmente un método largo es malo.
fuente
Historia verdadera. Una vez encontré un método que tenía más de dos mil líneas de largo. El método tenía regiones que describían lo que estaba haciendo en esas regiones. Después de leer una región, decidí hacer un método de extracción automatizado, nombrándolo de acuerdo con el nombre de la región. para cuando terminé, el método no era más que 40 llamadas al método de aproximadamente cincuenta líneas cada una y todo funcionó igual.
Lo que es demasiado grande es subjetivo. A veces, un método no puede desglosarse más allá de lo que es actualmente. Es como escribir un libro. La mayoría de la gente está de acuerdo en que los párrafos largos generalmente deben dividirse. Pero a veces, solo hay una idea y dividirla causa más confusión de la que causaría la longitud de ese párrafo.
fuente
El objetivo de un método es ayudar a reducir la regurgitación del código. Un método debe tener una función específica de la que es responsable. Si termina volviendo a escribir código en numerosos lugares, corre el riesgo de que el código tenga resultados inesperados si se cambian las especificaciones que el software está diseñado para abordar.
Para que un método tenga 350 líneas sugeriría que muchas de las tareas que está llevando a cabo se replican en otro lugar, ya que es inusual requerir una cantidad tan grande de código para llevar a cabo una tarea especializada.
fuente
No son realmente los métodos largos los que son una mala práctica, es más bien dejarlos así que es malo.
Quiero decir que el acto real de refactorizar su muestra de:
a
y luego a
ahora está en camino no solo a un método mucho más corto, sino a uno mucho más útil y comprensible.
fuente
Creo que el hecho de que el método es muy largo es algo para verificar, pero definitivamente no es un antipatrón instantáneo. La gran cosa a tener en cuenta en los métodos enormes es un montón de anidamiento. Si usted tiene
y el cuerpo del bucle no está extremadamente localizado (es decir, podría enviarle menos de 4 parámetros), entonces probablemente sea mejor convertirlo a:
A su vez, esto puede reducir enormemente la duración de una función. Además, asegúrese de buscar código duplicado en la función y moverlo a una función separada
Finalmente, algunos métodos son largos y complicados y no hay nada que pueda hacer. Algunos problemas necesitan soluciones que no se prestan a un código fácil. Por ejemplo, analizar una gramática de mucha complejidad puede hacer métodos realmente largos que realmente no se pueden hacer sin empeorarlo.
fuente
La verdad es que depende. Como se mencionó, si el código no separa las preocupaciones e intenta hacer todo en un método, entonces es un problema. La separación del código en múltiples módulos facilita la lectura del código, así como la escritura del código (por parte de múltiples programadores). Para empezar, es una buena idea adherirse a un módulo (clase) por archivo fuente.
En segundo lugar, cuando se trata de funciones / procedimientos:
es un buen método si comprueba el rango de solo "Datos". Es un método MALO cuando se aplica el mismo rango para múltiples funciones (ejemplo de código incorrecto):
Esto debe ser refactorizado para:
REUTILIZA el código tanto como sea posible. Y eso es posible cuando cada función de su programa es SIMPLE (no necesariamente fácil).
CITA: LA MONTAÑA está compuesta de pequeños granos de tierra. El océano está formado por pequeñas gotas de agua .. (- Sivananda)
fuente
Los métodos largos tienden a ser "malos" en los lenguajes imperativos que favorecen las declaraciones, los efectos secundarios y la mutabilidad, precisamente porque esas características aumentan la complejidad y, por lo tanto, los errores.
En los lenguajes de programación funcionales, que favorecen las expresiones, la pureza y la inmutabilidad, hay menos motivos de preocupación.
En lenguajes funcionales e imperativos por igual, siempre es mejor factorizar fragmentos de código reutilizables en rutinas comunes de nivel superior, pero en lenguajes funcionales que admiten el alcance léxico con funciones anidadas, etc., en realidad es mejor encapsulación para ocultar subrutinas dentro de la parte superior -nivel (método) que dividirlos en otras funciones de nivel superior.
fuente