Clean Code: Capítulo 3
TLTR: No pasa nada si en un primer momento nuestras funciones no son perfectas, hay un montón de buenas prácticas que podemos seguir para mejorarlas.
Funciones
Las funciones con mayor grado de abstracción representan un gran concepto que es posible dividir en un subconjunto de pasos. Estos pasos a su vez se tratan de funciones que representan un concepto, pero ahora menos abstractas; y así sucesivamente.
Estas funciones deben ser muy cortas, de apenas 20 líneas, y hacer sólo una cosa, de esta manera será mucho más sencillo elegir un buen nombre para ellas. Una forma de averiguar si una función hace más de una cosa es intentar extraer de ella otra función y ponerle un nombre que no signifique lo mismo que el original. Otra forma es asegurarse de que todos los conceptos de la función estén en el mismo nivel de abstracción.
Argumentos
En cuanto al número de argumentos que recibe una función, lo ideal es que no tenga ninguno. Aun así, hay tres principales motivos para que una función reciba un argumento:
- Se pregunta algo sobre ese argumento y se devuelve un booleano.
- Ese argumento se va a transformar, y el resultado es el valor de retorno.
- La función es un evento y altera el estado del sistema. No devuelve nada.
Otras formas deberían evitarse. Por ejemplo, recibir un flag como argumento.
Las funciones con dos argumentos también son aceptables, aunque son más difíciles de entender a primera vista y es necesario recordar el orden en el que van esos argumentos. Algunas se pueden convertir en funciones de un argumento haciendo que uno de los argumentos sea un atributo de la clase, por ejemplo.
Cuando una función deba recibir tres o más argumentos, es muy posible que algunos de ellos representen un concepto independiente y puedan encapsularse en una clase. Además, si una función tiene muchos argumentos, a la hora de escribir los tests habría que contemplar un mayor número de combinaciones posibles.
Bloques
El contenido de un bloque (if, else, for, while, … ) debería ocupar solamente una línea, que probablemente sea una llamada a otra función; así que el nivel de indentación en una función no debería ser mayor que uno o dos.
El caso la sentencia switch es particular. Generalmente estos bloques son muy largos y hacen más de una cosa, y aunque no siempre los podemos evitar, podemos al menos asegurarnos de que no se repitan y que estén en las clases más concretas. Sin embargo, esto dependerá de cada caso.
Excepciones
Al usar excepciones en lugar de devolver códigos de error, y por lo tanto usar los bloques try/catch, el código necesario para gestionar el error queda separado del resto del código facilitando la lectura del mismo.
El contenido de los bloques try/catch se deber extraer y ser una función independiente.
En conclusión, que una función cumpla su objetivo y devuelva el resultado deseado no significa que sea un buen código.
De hecho, nuestras funciones no serán perfectas desde el principio. Sus primeras versiones serán largas y complicadas de entender, con código duplicado y largas listas de argumentos. Es entonces cuando habría que aplicar todos los conceptos mencionados anteriormente para obtener un código de mejor calidad.