Tengo un método grande que realiza 3 tareas, cada una de ellas se puede extraer en una función separada. Si realizaré funciones adicionales para cada una de esas tareas, ¿mejorará o empeorará mi código y por qué?
Obviamente, tendrá menos líneas de código en la función principal, pero habrá declaraciones de funciones adicionales, por lo que mi clase tendrá métodos adicionales, lo que creo que no es bueno, porque hará que la clase sea más compleja.
¿Debo hacer eso antes de escribir todo el código o debo dejarlo hasta que todo esté hecho y luego extraer las funciones?
java
design-patterns
dhblah
fuente
fuente
Respuestas:
Este es un libro que a menudo enlazo, pero aquí voy de nuevo: el Código Limpio de Robert C. Martin , capítulo 3, "Funciones".
¿Prefiere leer una función con +150 líneas, o una función que llame a 3 +50 líneas? Creo que prefiero la segunda opción.
Sí , mejorará su código en el sentido de que será más "legible". Haga funciones que realicen una y solo una cosa, serán más fáciles de mantener y producir un caso de prueba.
Además, una cosa muy importante que aprendí con el libro antes mencionado: elija nombres buenos y precisos para sus funciones. Cuanto más importante es la función, más preciso debe ser el nombre. No se preocupe por la longitud del nombre, si debe llamarse
FunctionThatDoesThisOneParticularThingOnly
, asígnele el nombre de esa manera.Antes de realizar su refactorización, escriba uno o más casos de prueba. Asegúrate de que funcionen. Una vez que haya terminado con su refactorización, podrá iniciar estos casos de prueba para asegurarse de que el nuevo código funcione correctamente. Puede escribir pruebas "más pequeñas" adicionales para garantizar que sus nuevas funciones funcionen bien separadamente.
Finalmente, y esto no es contrario a lo que acabo de escribir, pregúntese si realmente necesita hacer esta refactorización, consulte las respuestas a "¿ Cuándo refactorizar ?" (también, busque SO preguntas sobre "refactorización", hay más y las respuestas son interesantes para leer)
Si el código ya está allí y funciona y tiene poco tiempo para la próxima versión, no lo toque. De lo contrario, creo que uno debería hacer pequeñas funciones siempre que sea posible y, como tal, refactorizar cada vez que haya tiempo disponible y asegurarse de que todo funcione como antes (casos de prueba).
fuente
Si obviamente. Si es fácil ver y separar las diferentes "tareas" de una sola función.
Pero puede haber problemas con esto:
fuente
El problema que plantea no es un problema de codificación, convenciones o práctica de codificación, sino un problema de legibilidad y formas en que los editores de texto muestran el código que escribe. Este mismo problema aparece también en la publicación:
¿Está bien dividir las funciones y los métodos largos en otros más pequeños a pesar de que nada más los llamará?
Dividir una función en subfunciones tiene sentido al implementar un gran sistema con la intención de encapsular las diferentes funcionalidades de las que estará compuesto. Sin embargo, tarde o temprano, te encontrarás con una serie de grandes funciones. Algunos de ellos son ilegibles y difíciles de mantener, ya sea que los mantenga como funciones largas individuales o las divida en funciones más pequeñas. Esto es particularmente cierto para las funciones donde las operaciones que realiza no son necesarias en ningún otro lugar de su sistema. Permite recoger una de estas funciones tan largas y considerarla en una vista más amplia.
Pro:
Contra:
Ahora imaginemos dividir la función larga en varias subfunciones y analizarlas con una perspectiva más amplia.
Pro:
Contra:
Ambas soluciones tienen pro y contra. La mejor solución real sería tener editores que permitan expandir, en línea y en toda su profundidad, cada función llamada en su contenido. Lo que haría que la división de funciones en subfunciones sea la mejor solución.
fuente
Para mí hay cuatro razones para extraer bloques de código en funciones:
Lo está reutilizando : acaba de copiar un bloque de código en el portapapeles. En lugar de pegarlo, póngalo en una función y reemplace el bloque con una llamada a función en ambos lados. Entonces, cada vez que necesite cambiar ese bloque de código, solo necesita cambiar esa única función en lugar de cambiar el código en varios lugares. Entonces, cada vez que copie un bloque de código, debe hacer una función.
Es una devolución de llamada : es un controlador de eventos o algún tipo de código de usuario que llama una biblioteca o un marco. (Apenas puedo imaginar esto sin hacer funciones).
Cree que se reutilizará , en el proyecto actual o tal vez en otro lugar: acaba de escribir un bloque que calcula la subsecuencia común más larga de dos matrices. Incluso si su programa llama a esta función solo una vez, creo que necesitaré esta función eventualmente en otros proyectos también.
Desea un código autodocumentado : por lo tanto, en lugar de escribir una línea de comentario sobre un bloque de código que resuma lo que hace, extrae todo en una función y lo nombra como lo que escribiría en un comentario. Aunque no soy fanático de esto, porque me gusta escribir el nombre del algoritmo utilizado, la razón por la que elegí ese algoritmo, etc. Los nombres de las funciones serían demasiado largos entonces ...
fuente
Estoy seguro de que ha escuchado el consejo de que las variables deben tener un alcance lo más estricto posible, y espero que esté de acuerdo con eso. Bueno, las funciones son contenedores de alcance, y en funciones más pequeñas el alcance de las variables locales es más pequeño. Es mucho más claro cómo y cuándo se supone que deben usarse y es más difícil usarlos en el orden incorrecto o antes de que se inicialicen.
Además, las funciones son contenedores de flujo lógico. Solo hay una entrada, las salidas están claramente marcadas, y si la función es lo suficientemente corta, los flujos internos deberían ser obvios. Esto tiene el efecto de reducir la complejidad ciclomática, que es una forma confiable de reducir la tasa de defectos.
fuente
Aparte: escribí esto en respuesta a la pregunta de Dallin (ahora cerrada) pero todavía siento que podría ser útil para alguien, así que aquí va
Creo que la razón para atomizar las funciones es 2 veces mayor, y como menciona @jozefg, depende del lenguaje utilizado.
Separación de intereses
La razón principal para hacer esto es mantener diferentes partes de código separadas, por lo que cualquier bloque de código que no contribuya directamente al resultado / intento deseado de la función es una preocupación separada y podría extraerse.
Supongamos que tiene una tarea en segundo plano que también actualiza una barra de progreso, la actualización de la barra de progreso no está directamente relacionada con la tarea de ejecución prolongada, por lo que debe extraerse, incluso si es el único fragmento de código que usa la barra de progreso.
Digamos que en JavaScript tiene una función getMyData (), que 1) crea un mensaje de jabón a partir de parámetros, 2) inicializa una referencia de servicio, 3) llama al servicio con el mensaje de jabón, 4) analiza el resultado, 5) devuelve el resultado. Parece razonable, he escrito esta función exacta muchas veces, pero en realidad eso podría dividirse en 3 funciones privadas que solo incluyen el código para 3 y 5 (si es así) ya que ninguno de los otros códigos es directamente responsable de obtener datos del servicio .
Experiencia de depuración mejorada
Si tiene funciones completamente atómicas, su seguimiento de pila se convierte en una lista de tareas, que enumera todo el código ejecutado con éxito, es decir:
sería mucho más interesante que descubrir que hubo un error al obtener los datos. Pero algunas herramientas son aún más útiles para depurar árboles de llamadas detallados que, por ejemplo, tomar Microsofts Debugger Canvas .
También entiendo su preocupación de que puede ser difícil seguir el código escrito de esta manera porque al final del día, debe elegir un orden de funciones en un solo archivo donde su árbol de llamadas sería mucho más complejo que eso. . Pero si las funciones se nombran bien (intellisense me permite usar 3-4 palabras mayúsculas y minúsculas en cualquier función, por favor, sin retrasarme) y estructurado con una interfaz pública en la parte superior del archivo, su código se leerá como un pseudocódigo que es, con mucho, la forma más fácil de obtener una comprensión de alto nivel de una base de código.
FYI: esta es una de esas cosas de "haz lo que digo, no lo que hago", mantener el código atómico no tiene sentido a menos que seas implacablemente coherente con eso, en mi humilde opinión, lo que no soy.
fuente