35 líneas, 55 líneas, 100 líneas, 300 líneas? ¿Cuándo deberías comenzar a romperlo? Lo pregunto porque tengo una función con 60 líneas (incluidos los comentarios) y estaba pensando en separarla.
long_function(){ ... }
dentro:
small_function_1(){...}
small_function_2(){...}
small_function_3(){...}
Las funciones no se utilizarán fuera de la función long_function, hacer funciones más pequeñas significa más llamadas de función, etc.
¿Cuándo separarías una función en otras más pequeñas? ¿Por qué?
- Los métodos deben hacer solo una cosa lógica (piense en la funcionalidad)
- Deberías poder explicar el método en una sola oración
- Debe caber en la altura de su pantalla
- Evite gastos innecesarios (comentarios que señalan lo obvio ...)
- Las pruebas unitarias son más fáciles para pequeñas funciones lógicas.
- Compruebe si parte de la función puede ser reutilizada por otras clases o métodos
- Evite el acoplamiento excesivo entre clases
- Evite estructuras de control profundamente anidadas
Gracias a todos por las respuestas , edite la lista y vote por la respuesta correcta. Elegiré esa;)
Estoy refactorizando ahora con esas ideas en mente :)
function
refactoring
coding-style
usuario58163
fuente
fuente
Respuestas:
No hay reglas realmente duras y rápidas para ello. En general, me gustan mis métodos para "hacer una cosa". Entonces, si se trata de capturar datos, luego hacer algo con esos datos, luego escribirlos en el disco y luego dividir el capturar y escribir en métodos separados para que mi método "principal" solo contenga el "hacer algo".
Sin embargo, ese "hacer algo" podría ser un buen número de líneas, así que no estoy seguro de que varias líneas sean la métrica correcta para usar :)
Editar: Esta es una sola línea de código que envié por correo la semana pasada (para probar un punto ... no es algo de lo que me acostumbre :)) - Ciertamente no querría 50-60 de estos chicos malos en mi método :RE
fuente
Aquí hay una lista de banderas rojas (sin ningún orden en particular) que podrían indicar que una función es demasiado larga:
Estructuras de control profundamente anidadas : por ejemplo, for-loops de 3 niveles de profundidad o incluso solo 2 niveles de profundidad con sentencias if anidadas que tienen condiciones complejas.
Demasiados parámetros que definen el estado : por parámetro que define el estado , me refiero a un parámetro de función que garantiza una ruta de ejecución particular a través de la función. Obtenga demasiados de estos tipos de parámetros y tendrá una explosión combinatoria de rutas de ejecución (esto generalmente ocurre en conjunto con el n. ° 1).
Lógica que se duplica en otros métodos : la reutilización de código deficiente es un gran contribuyente al código de procedimiento monolítico. Gran parte de esa duplicación lógica puede ser muy sutil, pero una vez refactorizado, el resultado final puede ser un diseño mucho más elegante.
Acoplamiento entre clases excesivo : esta falta de encapsulación adecuada da como resultado funciones relacionadas con las características íntimas de otras clases, por lo tanto, las alarga.
Sobrecarga innecesaria : los comentarios que señalan las clases obvias, profundamente anidadas, captadores y establecedores superfluos para variables de clase anidadas privadas y nombres de funciones / variables inusualmente largos pueden crear ruido sintáctico dentro de funciones relacionadas que finalmente aumentarán su longitud.
Su pantalla masiva de grado de desarrollador no es lo suficientemente grande como para mostrarla : en realidad, las pantallas de hoy en día son lo suficientemente grandes como para que una función que esté cerca de su altura sea probablemente demasiado larga. Pero, si es más grande , es una pistola humeante que algo está mal.
No se puede determinar de inmediato el propósito de la función : Por otra parte, una vez que realmente haces determinar su propósito, si no se puede resumir este propósito en una sola frase o sucede que tiene un tremendo dolor de cabeza, esto debería ser una pista.
En conclusión, las funciones monolíticas pueden tener consecuencias de largo alcance y, a menudo, son un síntoma de importantes deficiencias de diseño. Cada vez que encuentro un código que es una alegría absoluta para leer, su elegancia es evidente de inmediato. Y adivina qué: las funciones son a menudo muy cortas.
fuente
// fetch Foo's credentials where Bar is "uncomplete"
. Es casi seguro que es un nombre de función allí mismo y debe desacoplarse. Probablemente quiera ser refactorizado en algo como:Foo.fetchCredentialWhereBarUncomplete()
Creo que hay una gran advertencia para el mantra "haz una sola cosa" en esta página. A veces, hacer una cosa hace malabares con muchas variables. No divida una función larga en un grupo de funciones más pequeñas si las funciones más pequeñas terminan teniendo largas listas de parámetros. Hacer eso solo convierte una sola función en un conjunto de funciones altamente acopladas sin ningún valor individual real.
fuente
Una función debe hacer solo una cosa. Si está haciendo muchas cosas pequeñas en una función, haga que cada cosa pequeña sea una función y llame a esas funciones desde la función larga.
Lo que realmente no desea hacer es copiar y pegar cada 10 líneas de su función larga en funciones cortas (como sugiere su ejemplo).
fuente
Estoy de acuerdo en que una función solo debe hacer una cosa, pero a qué nivel es esa única cosa.
Si sus 60 líneas logran una cosa (desde la perspectiva de sus programas) y las piezas que conforman esas 60 líneas no serán utilizadas por otra cosa, entonces 60 líneas está bien.
No hay un beneficio real en dividirlo, a menos que pueda dividirlo en piezas de concreto que se mantengan solas. La métrica a usar es la funcionalidad y no las líneas de código.
He trabajado en muchos programas en los que los autores llevaron la única cosa a un nivel extremo y todo lo que terminó haciendo fue que pareciera que alguien llevó una granada a una función / método y la explotó en docenas de piezas desconectadas que son difícil de seguir.
Al extraer partes de esa función, también debe tener en cuenta si agregará una sobrecarga innecesaria y evitar pasar grandes cantidades de datos.
Creo que el punto clave es buscar la reutilización en esa función larga y extraer esas partes. Lo que le queda es la función, ya sea de 10, 20 o 60 líneas de largo.
fuente
60 líneas es grande pero no demasiado larga para una función. Si cabe en una pantalla en un editor, puede verlo todo de una vez. Realmente depende de lo que esté haciendo las funciones.
Por qué puedo romper una función:
fuente
DoThisAndThisAndAlsoThis
función, pero con mucha abstracción que todavía tiene que nombrar de alguna maneraMi heurística personal es que es demasiado largo si no puedo ver todo sin desplazarme.
fuente
Tamaño aproximado de su tamaño de pantalla (así que obtenga una gran pantalla panorámica giratoria y gírela) ... :-)
Broma aparte, una cosa lógica por función.
Y lo positivo es que las pruebas unitarias son mucho más fáciles de hacer con pequeñas funciones lógicas que hacen 1 cosa. ¡Las funciones grandes que hacen muchas cosas son más difíciles de verificar!
/ Johan
fuente
Regla general: si una función contiene bloques de código que hacen algo, que está algo separado del resto del código, colóquelo en una función separada. Ejemplo:
mucho más bonito:
Este enfoque tiene dos ventajas:
Siempre que necesite buscar direcciones para un determinado código postal, puede usar la función fácilmente disponible.
Cuando necesite volver a leer la función
build_address_list_for_zip()
, sabrá lo que hará el primer bloque de código (obtiene direcciones para un determinado código postal, al menos eso es lo que puede derivar del nombre de la función). Si hubiera dejado el código de consulta en línea, primero necesitaría analizar ese código.[Por otro lado (negaré que te dije esto, incluso bajo tortura): si lees mucho sobre la optimización de PHP, podrías tener la idea de mantener el número de funciones lo más pequeño posible, porque las llamadas a funciones son muy, Muy caro en PHP. No sé sobre eso ya que nunca hice ningún punto de referencia. Si ese fuera el caso, probablemente sería mejor no seguir ninguna de las respuestas a su pregunta si su aplicación es muy "sensible al rendimiento" ;-)]
fuente
Eche un vistazo al ciclomático de McCabe, en el que divide su código en un gráfico donde, "Cada nodo en el gráfico corresponde a un bloque de código en el programa donde el flujo es secuencial y los arcos corresponden a las ramas tomadas en el programa. "
Ahora imagine que su código no tiene funciones / métodos; es solo una enorme extensión de código en forma de gráfico.
Desea dividir esta expansión en métodos. Tenga en cuenta que, cuando lo haga, habrá un cierto número de bloques en cada método. Solo un bloque de cada método será visible para todos los demás métodos: el primer bloque (suponemos que podrá saltar a un método en un solo punto: el primer bloque). Todos los demás bloques en cada método serán información oculta dentro de ese método, pero cada bloque dentro de un método puede saltar potencialmente a cualquier otro bloque dentro de ese método.
Para determinar qué tamaño deben tener sus métodos en términos de número de bloques por método, una pregunta que puede hacerse es: ¿cuántos métodos debo tener para minimizar el número máximo potencial de dependencias (MPE) entre todos los bloques?
Esa respuesta está dada por una ecuación. Si r es el número de métodos que minimiza el MPE del sistema, yn es el número de bloques en el sistema, entonces la ecuación es: r = sqrt (n)
Y se puede demostrar que esto da el número de bloques por método para ser, también, sqrt (n).
fuente
Tenga en cuenta que puede terminar refactorizando solo por refactorizar, lo que puede hacer que el código sea más ilegible de lo que era en primer lugar.
¡Un antiguo colega mío tenía una extraña regla de que una función / método solo debe contener 4 líneas de código! Intentó apegarse a esto de manera tan rígida que los nombres de sus métodos a menudo se volvieron repetitivos y sin sentido, además las llamadas se volvieron profundamente anidadas y confusas.
Entonces, mi propio mantra se ha convertido: si no puede pensar en un nombre de función / método decente para la porción de código que está re-factorizando, no se moleste.
fuente
La razón principal por la que generalmente rompo una función es porque partes de ella también son ingredientes en otra función cercana que estoy escribiendo, por lo que las partes comunes se tienen en cuenta. Además, si está utilizando muchos campos o propiedades de alguna otra clase, hay una buena posibilidad de que la porción relevante se pueda retirar al por mayor y, si es posible, trasladarse a la otra clase.
Si tiene un bloque de código con un comentario en la parte superior, considere convertirlo en una función, con los nombres de función y argumento que ilustran su propósito, y reservando el comentario para la justificación del código.
¿Estás seguro de que no hay piezas allí que puedan ser útiles en otro lugar? ¿Qué tipo de función es?
fuente
En mi opinión, la respuesta es: cuando hace demasiadas cosas. Su función debe realizar solo las acciones que espera del nombre de la función misma. Otra cosa a tener en cuenta es si desea reutilizar algunas partes de sus funciones en otras; en este caso podría ser útil dividirlo.
fuente
Por lo general, rompo las funciones por la necesidad de colocar comentarios que describan el siguiente bloque de código. Lo que antes entraba en los comentarios ahora va al nuevo nombre de la función. Esta no es una regla difícil, pero (para mí) es una buena regla general. Me gusta que el código hable por sí mismo mejor que uno que necesita comentarios (como he aprendido, los comentarios generalmente mienten)
fuente
Esto es en parte una cuestión de gustos, pero la forma en que lo determine es que trato de mantener mis funciones aproximadamente solo el tiempo que quepa en mi pantalla al mismo tiempo (como máximo). La razón es que es más fácil entender lo que está sucediendo si puedes ver todo de una vez.
Cuando codifico, es una mezcla de escribir funciones largas, luego refactorizar para extraer bits que podrían ser reutilizados por otras funciones, y escribir pequeñas funciones que realizan tareas discretas a medida que avanzo.
No sé si hay una respuesta correcta o incorrecta a esto (por ejemplo, puede conformarse con 67 líneas como su máximo, pero puede haber ocasiones en que tenga sentido agregar algunas más).
fuente
Se ha realizado una investigación exhaustiva sobre este mismo tema, si desea la menor cantidad de errores, su código no debería ser demasiado largo. Pero tampoco debería ser demasiado corto.
No estoy de acuerdo con que un método debe caber en su pantalla en uno, pero si se desplaza hacia abajo en más de una página, entonces el método es demasiado largo.
Consulte El tamaño de clase óptimo para el software orientado a objetos para obtener más información.
fuente
He escrito 500 funciones de línea antes, sin embargo, estas fueron solo grandes declaraciones para decodificar y responder mensajes. Cuando el código para un solo mensaje se volvió más complejo que un solo si-entonces-otro, lo extraje.
En esencia, aunque la función era de 500 líneas, las regiones mantenidas independientemente promediaron 5 líneas.
fuente
Normalmente uso un enfoque basado en pruebas para escribir código. En este enfoque, el tamaño de la función a menudo está relacionado con la granularidad de sus pruebas.
Si su prueba está lo suficientemente enfocada, entonces lo llevará a escribir una pequeña función enfocada para hacer que la prueba pase.
Esto también funciona en la otra dirección. Las funciones deben ser lo suficientemente pequeñas como para probar de manera efectiva. Entonces, cuando trabajo con código heredado, a menudo encuentro que desgloso funciones más grandes para probar las diferentes partes de ellas.
Usualmente me pregunto "cuál es la responsabilidad de esta función" y si no puedo expresar la responsabilidad en una oración clara y concisa, y luego la traduzco en una pequeña prueba enfocada, me pregunto si la función es demasiado grande.
fuente
Si tiene más de tres ramas, generalmente esto significa que una función o método debe separarse para encapsular la lógica de ramificación en diferentes métodos.
Cada bucle for, instrucción if, etc., no se ve como una rama en el método de llamada.
Cobertura para el código Java (y estoy seguro de que hay otras herramientas para otros idiomas) calcula el número de if, etc. en una función para cada función y lo suma para la "complejidad ciclomática promedio".
Si una función / método solo tiene tres ramas, obtendrá un tres en esa métrica, lo cual es muy bueno.
A veces es difícil seguir esta directriz, a saber, para validar la entrada del usuario. Sin embargo, poner ramas en diferentes métodos ayuda no solo al desarrollo y el mantenimiento, sino también a las pruebas, ya que las entradas a los métodos que realizan la ramificación se pueden analizar fácilmente para ver qué entradas deben agregarse a los casos de prueba para cubrir las ramas que No estaban cubiertos.
Si todas las ramas estuvieran dentro de un solo método, las entradas tendrían que ser rastreadas desde el inicio del método, lo que dificulta la capacidad de prueba.
fuente
Sospecho que encontrarás muchas respuestas sobre esto.
Probablemente lo dividiría en función de las tareas lógicas que se realizaban dentro de la función. Si le parece que su cuento se está convirtiendo en una novela, sugeriría encontrar y extraer pasos distintos.
Por ejemplo, si tiene una función que maneja algún tipo de entrada de cadena y devuelve un resultado de cadena, puede dividir la función en función de la lógica para dividir su cadena en partes, la lógica para agregar caracteres adicionales y la lógica para ponerla todos juntos de nuevo como resultado formateado.
En resumen, lo que sea que haga que su código sea limpio y fácil de leer (ya sea simplemente asegurando que su función tenga buenos comentarios o dividiéndolo) es el mejor enfoque.
fuente
suponiendo que está haciendo una cosa, la duración dependerá de:
60 líneas pueden ser demasiado largas o estar bien. Sin embargo, sospecho que puede ser demasiado largo.
fuente
Una cosa (y esa cosa debería ser obvia por el nombre de la función), pero no más que una pantalla llena de código, independientemente. Y siéntase libre de aumentar el tamaño de su fuente. Y si tiene dudas, refactorícelo en dos o más funciones.
fuente
Extendiendo el espíritu de un tweet del tío Bob hace un tiempo, sabes que una función se está alargando demasiado cuando sientes la necesidad de poner una línea en blanco entre dos líneas de código. La idea es que si necesita una línea en blanco para separar el código, su responsabilidad y alcance se están separando en ese punto.
fuente
Mi idea es que si tengo que preguntarme si es demasiado largo, probablemente sea demasiado largo. Ayuda a hacer funciones más pequeñas, en esta área, porque podría ayudar más adelante en el ciclo de vida de la aplicación.
fuente