¿Cuál es la longitud ideal de un método para usted? [cerrado]

107

En la programación orientada a objetos, por supuesto, no existe una regla exacta sobre la longitud máxima de un método, pero aún así estas dos citas se contradicen entre sí, por lo que me gustaría escuchar lo que piensas.

En Clean Code: Un manual de artesanía de software ágil , Robert Martin dice :

  

La primera regla de las funciones es que deben ser pequeñas. El segundo   La regla de las funciones es que deberían ser más pequeñas que eso. Funciones   No debe tener 100 líneas de largo. Las funciones casi nunca deben ser de 20 líneas.   largo.

y da un ejemplo del código Java que ve de Kent Beck:

  

Cada función en su programa era solo dos, tres o cuatro líneas.   largo. Cada uno era transparente y obvio. Cada uno contaba una historia. Y cada led   Usted al siguiente en un orden convincente. Así de cortas son tus funciones.   debería ser!

Esto suena bien, pero por otro lado, en Code Complete , Steve McConnell dice algo muy diferente:

  

Se debe permitir que la rutina crezca orgánicamente hasta 100-200 líneas,   décadas de evidencia dicen que las rutinas de tal longitud no más error   propensas a las rutinas más cortas.

Y él hace referencia a un estudio que dice que las rutinas de 65 líneas o más son más baratas de desarrollar.

Entonces, si bien hay opiniones divergentes sobre el asunto, ¿existe alguna práctica práctica óptima para usted?

    
pregunta iPhoneDeveloper 05.02.2012 - 11:26
fuente

14 respuestas

103

Las funciones normalmente deben ser cortas, entre 5 y 15 líneas es mi "regla de oro" personal cuando se codifica en Java o C #. Este es un buen tamaño por varias razones:

  • Cabe fácilmente en su pantalla sin desplazarse
  • Se trata del tamaño conceptual que puedes tener en tu cabeza
  • Es lo suficientemente significativo como para requerir una función por derecho propio (como un trozo de lógica independiente y significativo)
  • Una función más pequeña que 5 líneas es un indicio de que quizás esté rompiendo el código demasiado (lo que dificulta la lectura / comprensión si necesita navegar entre las funciones). ¡O eso o estás olvidando tus casos especiales / manejo de errores!

Pero no creo que sea útil establecer una regla absoluta, ya que siempre habrá excepciones / razones válidas para desviarse de la regla:

  • Una función de acceso de una línea que realiza una conversión de tipos es claramente aceptable en algunas situaciones.
  • Hay algunas funciones muy cortas pero útiles (por ejemplo, el intercambio como lo menciona el usuario desconocido) que claramente necesita menos de 5 líneas. No es un gran problema, unas pocas funciones de 3 líneas no dañan su base de código.
  • Una función de 100 líneas que es una sola instrucción de conmutación grande podría ser aceptable si está extremadamente claro lo que se está haciendo. Este código puede ser conceptualmente muy simple incluso si requiere muchas líneas para describir los diferentes casos. A veces se sugiere que esto se vuelva a ajustar en clases separadas y se implemente usando herencia / polimorfismo pero, en mi humilde opinión, esto está llevando la POO demasiado lejos. Prefiero tener una gran declaración de cambio de 40 vías en lugar de 40 clases nuevas para tratar.
  • Una función compleja puede tener muchas variables de estado que se pueden ensuciar mucho si se pasan entre diferentes funciones como parámetros. En este caso, podría razonablemente argumentar que el código es más simple y más fácil de seguir si mantiene todo en una sola función grande (aunque Mark señala con razón que esto también podría ser un candidato para convertirse en una clase para encapsular tanto la lógica como la lógica). y estado)
  • A veces, las funciones más pequeñas o más grandes tienen ventajas de rendimiento (quizás debido a razones de alineación o JIT, como menciona Frank). Esto depende en gran medida de la implementación, pero puede marcar la diferencia. ¡Asegúrese de realizar una evaluación comparativa!

Básicamente, usa el sentido común , apégate a los tamaños de función pequeños en la mayoría de los casos, pero no seas dogmático al respecto si tienes una buena razón para hacer una función inusualmente grande.

    
respondido por el mikera 05.02.2012 - 11:44
fuente
28

Aunque estoy de acuerdo con los comentarios de otros cuando dicen que no hay una regla estricta sobre el número LOC correcto, apuesto a que si miramos hacia atrás a los proyectos que hemos visto en el pasado e identificamos cada función anterior, digamos 150 líneas de código , Supongo que llegaríamos a un consenso de que 9 de cada 10 de esas funciones rompen el SRP (y muy probablemente también el OCP), tienen demasiadas variables locales, demasiado flujo de control y generalmente son difíciles de leer y mantener.

Entonces, mientras que LOC puede no ser un indicador directo de código incorrecto, ciertamente es un indicador indirecto decente de que cierta función podría escribirse mejor.

En mi equipo caí en la posición de líder y por alguna razón, la gente parece escucharme. En lo general, me decidí a decirle al equipo que si bien no hay límite absoluto, cualquier función con más de 50 líneas de código debe, como mínimo, poner una bandera roja durante la revisión del código, de modo que lo examinemos por segunda vez y lo volvamos a evaluar Por complejidad y violaciones de SRP / OCP. Después de esa segunda mirada, podríamos dejarlo solo o cambiarlo, pero al menos hace que la gente piense sobre estas cosas.

    
respondido por el DXM 06.02.2012 - 04:36
fuente
19

Entré en un proyecto al que no le han importado las directrices de codificación. Cuando miro el código a veces encuentro clases con más de 6000 líneas de código y menos de 10 métodos. Este es un escenario de horror cuando tienes que corregir errores.

Una regla general de cuán grande debe ser un método al máximo a veces no es tan buena. Me gusta la regla de Robert C. Martin (tío Bob): "Los métodos deben ser pequeños, más pequeños que pequeños". Intento usar esta regla todo el tiempo. Intento mantener mis métodos simples y pequeños al aclarar que mi método solo hace una cosa y nada más.

    
respondido por el Smokefoot 05.02.2012 - 12:15
fuente
10

No se trata del número de líneas, se trata de SRP. De acuerdo con este principio, su método debe hacer una y solo una cosa.

Si su método hace esto Y esto Y esto O esto = > Probablemente está haciendo mucho. Intente ver este método y analice: "aquí obtengo estos datos, los ordeno y obtengo los elementos que necesito" y "aquí proceso estos elementos" y "aquí finalmente los combino para obtener el resultado". Estos "bloques" deben ser refactorizados a otros métodos.

Si simplemente sigue SRP, la mayoría de sus métodos serán pequeños y con clara intención.

No es correcto decir "este método es > 20 líneas, por lo que es incorrecto". Puede ser una indicación de que algo puede estar mal con este método, no más.

Es posible que tenga un conmutador de 400 líneas en un método (a menudo ocurre en las telecomunicaciones), y todavía es responsabilidad única y está perfectamente bien.

    
respondido por el Alexey 05.02.2012 - 13:51
fuente
8

Creo que un problema aquí es que la longitud de una función no dice nada sobre su complejidad. Las LOC (líneas de código) son un mal instrumento para medir cualquier cosa.

Un método no debe ser demasiado complejo, pero hay escenarios en los que se puede mantener fácilmente un método largo. Tenga en cuenta que el siguiente ejemplo no dice que no se pueda dividir en métodos, solo que los métodos no cambiarían la capacidad de mantenimiento.

por ejemplo, un manejador de datos entrantes puede tener una declaración de cambio grande y luego un código simple por caso. Tengo ese código: administrar los datos entrantes de una fuente. 70 (!) Manejadores codificados numéricamente. Ahora, uno dirá "usar constantes", sí, excepto que la API no las proporciona y me gusta estar cerca de "fuente" aquí. Métodos? Claro, lamentablemente, todos ellos tratan con datos de las mismas 2 enormes estructuras. No hay beneficio en separarlos, excepto que quizás tenga más métodos (legibilidad). El código es intrínsecamente no complejo, un interruptor, dependiendo de un campo. Luego, cada caso tiene un bloque que analiza x elementos de datos y los publica. No hay pesadilla de mantenimiento. Hay una repetición "si la condición determina si un campo tiene datos (pField = pFields [x], si pField- > IsSet () {blabla}): lo mismo para cada campo ...

Reemplace eso con una rutina mucho más pequeña que contenga un bucle anidado y una gran cantidad de declaraciones de conmutación reales, y un método enorme puede ser más fácil de mantener que una más pequeña.

Entonces, lo siento, LOC no es una buena medida para empezar. En todo caso, deberían utilizarse los puntos de complejidad / decisión.

    
respondido por el TomTom 05.02.2012 - 11:49
fuente
8

Depende , en serio, realmente no hay una respuesta sólida a esta pregunta porque el lenguaje con el que trabajas importa, las líneas de la quinceava a decimoquinta mencionadas en esta respuesta puede funcionar para C # o Java, pero en otros idiomas no le da mucho para trabajar. Del mismo modo, dependiendo del dominio en el que esté trabajando, es posible que se encuentre escribiendo valores de configuración de código en una gran estructura de datos. Con algunas estructuras de datos, es posible que tengas decenas de elementos que debes configurar, ¿deberías dividir las cosas en funciones separadas solo porque tu función es larga?

Como han señalado otros, la mejor regla de oro es que una función debe ser una entidad lógica única que maneje una sola tarea. Si intentas imponer reglas draconianas que dicen que las funciones no pueden ser más largas que las líneas n y haces ese valor demasiado pequeño, tu código se volverá más difícil de leer a medida que los desarrolladores intenten y utilicen trucos sofisticados para sortear la regla. Del mismo modo, si lo establece demasiado alto, no será un problema y puede llevar a un código incorrecto a pesar de la pereza. Lo mejor que puede hacer es simplemente realizar revisiones de código para asegurarse de que las funciones están manejando una sola tarea y dejarlo así.

    
respondido por el rjzii 06.02.2012 - 05:01
fuente
5

Si encuentro un método largo, podría apostar que este método no se ha probado correctamente en la unidad o la mayoría del tiempo no tiene ninguna prueba de unidad. Si comienza a hacer TDD, nunca acumulará métodos de 100 líneas con 25 responsabilidades diferentes y 5 bucles anidados. Las pruebas obligan a refactorizar constantemente su desorden y escribir el código de limpieza de Bob del tío.

    
respondido por el Sergii Shevchyk 11.04.2013 - 22:16
fuente
5

He estado en esta loca raqueta, de una forma u otra, desde 1970.

En todo ese tiempo, con dos excepciones a las que llegaré en un momento, NUNCA he visto una "rutina" bien diseñada (método, procedimiento, función, subrutina, lo que sea) que NECESITA ser más de una Página impresa (unas 60 líneas) de largo. La gran mayoría de ellos eran bastante cortos, del orden de 10-20 líneas.

Sin embargo, he visto MUCHO código de "flujo de conciencia", escrito por personas que aparentemente nunca han oído hablar de la modularización.

Las dos excepciones fueron casos muy especiales. Una de ellas es en realidad una clase de casos de excepción, que agrupan: autómatas de estados finitos de gran tamaño, implementados como enunciados de conmutadores grandes y feos, generalmente porque no hay una forma más clara de implementarlos. Estas cosas suelen aparecer en equipos de prueba automatizados, analizando los registros de datos del dispositivo bajo prueba.

El otro era la rutina de torpedos de fotones del juego STARTRK de Matuszek-Reynolds-McGehearty-Cohen, escrito en CDC 6600 FORTRAN IV. Tuvo que analizar la línea de comando, luego simular el vuelo de cada torpedo, con perturbaciones, verificar la interacción entre el torpedo y cada tipo de cosa que podría golpear, y por cierto simular la recursión para hacer conectividad de 8 vías en cadenas de Nova de torpedear una estrella que estaba al lado de otras estrellas.

    
respondido por el John R. Strohm 28.07.2014 - 20:26
fuente
4

Simplemente lanzaré otra cita.

  

Los programas deben estar escritos para que las personas los lean, y solo de forma incidental para   maquinas para ejecutar

     

- Harold Abelson

Es muy improbable que las funciones que aumentan a 100-200 sigan esta regla

    
respondido por el Pete 20.06.2014 - 14:17
fuente
1

No hay reglas absolutas sobre la longitud del método, pero las siguientes reglas han sido útiles:

  1. El propósito principal de la función es encontrar el valor de retorno. No hay otra razón para su existencia. Una vez que se complete ese motivo, no se debe insertar ningún otro código. Esto necesariamente mantiene las funciones pequeñas. La llamada a otras funciones solo debe hacerse si facilita la búsqueda del valor de retorno.
  2. Por otro lado, las interfaces deben ser pequeñas. Esto significa que tiene una gran cantidad de clases o funciones grandes: una de las dos ocurrirá una vez que comience a tener suficiente código para hacer algo significativo. Los grandes programas pueden tener ambos.
respondido por el tp1 05.02.2012 - 17:15
fuente
1

En mi humilde opinión, no debería tener que usar la barra de desplazamiento para leer su función. Tan pronto como necesite mover la barra de desplazamiento, tardará un poco más en comprender cómo funciona la función.

En consecuencia, depende del entorno de programación habitual del trabajo de su equipo (resolución de pantalla, editor, tamaño de fuente, etc.). En los años 80, era de 25 líneas y 80 columnas. Ahora, en mi editor, muestro casi 50 líneas. La cantidad de columnas que muestro no cambió desde que dividí mi pantalla en dos para mostrar dos archivos a veces.

En resumen, depende de la configuración de sus compañeros de trabajo.

    
respondido por el Jezz 05.07.2013 - 10:36
fuente
1

¿Los autores quieren decir lo mismo con "función" y "rutina"? Por lo general, cuando digo "función", me refiero a una subrutina / operación que devuelve un valor y un "procedimiento" para una que no (y cuya llamada se convierte en una sola declaración). Esta no es una distinción común en SE en el mundo real, pero la he visto en textos de usuarios.

De cualquier manera, no hay una respuesta correcta para esto. La preferencia por uno u otro (si existe alguna preferencia) es algo que esperaría que fuera muy diferente entre idiomas, proyectos y organizaciones; tal como es con todas las convenciones de código.

El bit que agregaría es que la afirmación de que "las operaciones largas no son más propensas a errores que las operaciones cortas" no es estrictamente cierta. Además del hecho de que más código equivale a más espacio de error potencial, es cegadoramente obvio que dividir el código en segmentos hará que los errores sean más fáciles de evitar y más fáciles de localizar. De lo contrario, no habría ninguna razón para dividir el código en absoluto, salvo la repetición. Pero esto quizás sea cierto solo si dichos segmentos están lo suficientemente documentados como para que pueda determinar los resultados de una llamada de operación sin leer o rastrear el código real (diseño por contrato basado en especificaciones en lugar de una dependencia concreta entre áreas de código).

Además, si desea que las operaciones más largas funcionen bien, es posible que desee adoptar convenciones de código más estrictas para respaldarlas. Lanzar una declaración de devolución en el medio de una operación puede estar bien para una operación corta, pero en operaciones más largas, esto puede crear una gran sección de código que es condicional pero no está condicionado obviamente a una lectura rápida (solo para un ejemplo).

Por lo tanto, creo que el estilo que tiene menos probabilidades de ser una pesadilla llena de errores dependerá en gran medida de las convenciones a las que se adhiera por el resto de su código. :)

    
respondido por el Trixie Wolf 28.07.2014 - 22:48
fuente
1

Creo que La respuesta de TomTom se acercó a mi opinión.

Cada vez más me encuentro con una complejidad ciclomática en lugar de líneas.

Normalmente aspiro a no más de una estructura de control por método, con la excepción de los muchos bucles necesarios para manejar una matriz multidimensional.

A veces me pongo ifs de una línea en los casos de cambio porque, por alguna razón, tienden a ser casos en los que dividirlo dificulta en lugar de ayudar.

Tenga en cuenta que no cuento la lógica de guarda contra este límite.

    
respondido por el Loren Pechtel 29.07.2014 - 06:42
fuente
-2

En el objeto OOP todas las cosas y hay estas características:

  1. polimorfismo
  2. abstracción
  3. Herencia

Cuando observas estas reglas, tus métodos generalmente son pequeños pero no existen reglas para reglas pequeñas o muy pequeñas (por ejemplo, 2-3 líneas). Un beneficio del método pequeño (unidad pequeña, por ejemplo, método o función) son:

  1. mejor legible
  2. mantener mejor
  3. error corregido mejor
  4. cambia mejor
respondido por el Sam 05.02.2012 - 11:45
fuente

Lea otras preguntas en las etiquetas