design - software - elementos de un diagrama de flujo de datos
Mejores prácticas: ¿muchas funciones/métodos pequeños, o funciones más grandes con componentes lógicos de proceso en línea? (11)
Algo que aprendí del libro The Code Complete:
- Escribir métodos / funciones para que implementen un trozo (o unidad o tarea) de lógica. Si eso requiere desglose en tareas secundarias, escriba un método / función independiente para ellas y llámelas.
- Si encuentro que el nombre del método / función se está volviendo largo, entonces trato de examinar el método para verlo, que se puede dividir en dos métodos.
Espero que esto ayude
¿Es mejor escribir muchos métodos pequeños (o funciones), o simplemente escribir la lógica / código de esos pequeños procesos directamente en el lugar donde habría llamado el método pequeño? ¿Qué hay de romper el código en una función pequeña, incluso si por el momento solo se llama desde un punto?
Si la elección de uno depende de algunos criterios, ¿qué son? ¿Cómo debería un programador hacer un buen juicio?
Espero que la respuesta se pueda aplicar generalmente en muchos idiomas, pero si es necesario, las respuestas dadas pueden ser específicas para un idioma o idiomas. En particular, estoy pensando en SQL (funciones, reglas y procedimientos almacenados), Perl, PHP, Javascript y Ruby.
Algunas reglas generales:
- Las funciones no deben ser más largas que las que se pueden mostrar en la pantalla
- Divida las funciones en otras más pequeñas si hace que el código sea más legible.
Como siempre puedes decir: depende. Es más una cuestión de nombrar y definir la tarea de un método. Cada método debe hacer una tarea (no más) bien definida y debe hacerlas por completo. El nombre del método debe indicar la tarea. Si su método se llama DoAandB (), puede ser mejor tener métodos separados DoA () y DoB (). Si necesita métodos como setupTask, executeTask, FinishTask, puede ser útil combinarlos.
Algunos puntos que indican que una combinación de diferentes métodos puede ser útil:
- Un método no puede usarse solo, sin el uso de otros métodos.
- Debe tener cuidado de llamar a algunos métodos dependientes en el orden correcto.
Algunos puntos que indican que una división del método podría ser útil:
- Algunas líneas del método existente tienen una clara tarea independiente.
- Las pruebas unitarias del gran método se vuelven problemáticas. Si las pruebas son más fáciles de escribir para métodos independientes, entonces divida el método grande.
Como explicación al argumento de prueba de unidad: escribí un método, que hizo algunas cosas, incluido IO. La parte IO fue muy difícil de probar, así que lo pensé. Llegué a la conclusión de que mi método hizo 5 pasos lógicos e independientes, y solo uno de ellos involucró el IO. Así que dividí mi método en 5 más pequeños, cuatro de ellos fueron fáciles de probar.
Cuanto mayor sea el método, más difícil de probar y mantener. Encuentro que es mucho más fácil entender cómo funciona un proceso grande cuando se divide en pasos atómicos. Además, hacer esto es un gran primer paso para hacer que tus clases sean extensibles. Puede marcar esos pasos individuales como virtuales (para herencia) o moverlos a otros objetos (composición), facilitando la personalización del comportamiento de la aplicación.
El tamaño del método está directamente relacionado con su complejidad ciclomática .
Las principales ventajas de mantener el tamaño del método pequeño (lo que significa dividir un método grande en varios métodos pequeños) son:
- mejores pruebas unitarias (debido a la baja complejidad ciclomática)
- mejor depuración debido a un seguimiento de pila más explícito (en lugar de un error en un método gigante)
Encuentro que tener muchos métodos pequeños hace que el código sea más fácil de leer, mantener y depurar.
Cuando leo una unidad que implementa cierta lógica comercial, puedo seguir mejor el flujo si veo una serie de llamadas a métodos que describen el proceso. Si me importa cómo se implementa el método, puedo buscar el código.
Se siente como más trabajo pero, en última instancia, ahorra tiempo.
Creo que hay un arte para saber qué encapsular. Todos tienen una ligera diferencia de opinión. Si pudiera expresarlo en palabras, diría que cada método debería hacer una cosa que pueda describirse como una tarea completa.
Hago que cada función haga una cosa, y una sola cosa, y trato de no anidar demasiados niveles de lógica. Una vez que comienzas a dividir tu código en funciones bien nombradas , se vuelve mucho más fácil de leer y prácticamente autodocumentado.
Pequeños métodos todo el tiempo.
Son autodocumentados (er, si están bien nombrados)
Desglosan el problema en partes manejables: usted está Manteniendo Simple.
Puede usar técnicas OO para un comportamiento de conexión más fácil (y obviamente). El método grande es, por definición, más procesal y, por lo tanto, menos flexible.
Son unidad comprobable. Este es el asesino, simplemente no puedes probar un método enorme que realiza una gran cantidad de tareas
Personalmente, me inclino significativamente en la dirección de preferir más métodos más pequeños, pero no hasta el punto de apuntar religiosamente a un recuento máximo de líneas. Mi criterio principal u objetivo es mantener mi código SECO. En el momento en que tengo un bloque de código que está duplicado (ya sea en espíritu o en realidad por el texto), incluso si tiene 2 o 4 líneas, RASGO ese código en un método diferente. Algunas veces lo haré con anticipación si creo que hay una buena posibilidad de que se vuelva a usar en el futuro.
Por otro lado, también he escuchado argumentar que si su método de separación es demasiado pequeño, en el contexto de un equipo de desarrolladores, es probable que un compañero de equipo no sepa sobre su método y escriba en línea o escriba su propio pequeño método que hace lo mismo. Esta es sin duda una mala situación.
Algunos también intentan argumentar que es más legible para mantener las cosas en línea, por lo que un lector puede simplemente leer de arriba hacia abajo, en lugar de tener que saltar alrededor de las definiciones de métodos, posiblemente a través de múltiples archivos. Personalmente, creo que la existencia de un seguimiento de pila hace que esto no sea un gran problema.
Siempre rompo los métodos largos en pedazos lógicos y trato de hacerles métodos más pequeños. Normalmente, no convierto algunas líneas en un método diferente hasta que lo necesito en dos lugares diferentes, pero a veces lo hago solo para ayudar a la legibilidad, o si quiero probarlo en forma aislada.
Refactoring de Fowler se trata de este tema, y lo recomiendo encarecidamente.
Aquí hay una práctica regla general que utilizo de Refactoring. Si una sección de código tiene un comentario que podría volver a escribir en un nombre de método, sáquelo y conviértalo en un método.
Suelo dividir las funciones en funciones más pequeñas que realizan una única tarea atómica, pero solo si esa función es lo suficientemente compleja como para evitarla.
De esta forma, no termino con funciones múltiples para tareas simples, y las funciones que extraigo pueden usarse en otros lugares, ya que no intentan lograr demasiado. Esto también ayuda a la prueba unitaria ya que cada función (como una acción lógica y atómica) se puede probar individualmente.