intive Argentina Blog

¿Qué es “Clean Code”? Aprendiendo a mejorar tu código con Kotlin

Qué complejo es programar. Y más aún, qué complejo es programar bien.

Para lo primero se necesita mucha práctica y pensamiento abstracto. Para lo segundo, se requiere aparte dedicación y disciplina. Pero y, si en lugar de separar en caminos distintos, ¿pensamos en ambas cosas como una sola?

Así es como surge el término “clean code”, acuñado por Robert C. Martin, conocido coloquialmente como el tío Bob, en su muy recomendable libro Clean Code.

¿Qué es “Clean Code”?

Se considera que el código es limpio (en inglés, clean code) cuando es fácil de leer y entender. Si resuelve los problemas sin agregar complejidad innecesaria, permitiendo que el mantenimiento o las adaptaciones, por algún cambio de requerimiento, sean tareas más sencillas, entonces estamos hablando de “clean code”.

Para crear código limpio hay que conocer y poner en práctica un conjunto de principios o técnicas de desarrollo que nos ayudaran a evitar los code smells, es decir, esos síntomas de un programa que te dan el indicio de que existe un problema más profundo.

Ahora explicaremos algunas de estas técnicas a través de ejemplos de código, para así observar la diferencia entre un código legible (que puede ser entendido por cualquier programador) y un código oscuro, que muchas veces solo puede ser entendido por la computadora.

Para los siguientes ejemplos utilizaremos Kotlin, un lenguaje de programación de tipado estático que corre sobre la máquina virtual de Java. Así demostraremos cómo sus características pueden ayudar significativamente a escribir un código más limpio.

1. Funciones pequeñas y nombres significativos

Una regla primordial es que las funciones siempre deben ser lo más pequeñas posibles y tener una única responsabilidad, con el fin de facilitar su comprensión y evitar que sean más propensas a los cambios.

Los nombres que se usen para las variables y las funciones deben ser claros y descriptivos para ayudar a que la lectura del código sea más fluida y favorecer un rápido entendimiento.

Como ejemplo, se dispone de una función que devuelve un listado de los mejores jugadores más jóvenes de un juego online. Se cuenta con las clases Winner, Player, Score y el enum Level que forman parte de las reglas del juego.

Se pueden observar varias fallas, tales como las muchas responsabilidades que presenta la función, su gran tamaño, algunos nombres confusos y el anidamiento profundo de los bloques condicionales que dificulta su comprensión.

Como primer paso, se refactorizará la función y se mostrará el resultado final. Posteriormente se explicará paso a paso cuáles criterios se utilizaron para ir mejorando el código junto con su resultado parcial.

Función refactorizada

Refactorización paso a paso

Se inicia la mejora del código dividiendo la función que se encarga del filtrado en dos funciones más pequeñas, cada una con su propia lógica.

Las funciones filter reciben lambdas, que son muy similares a las funciones anónimas. Cuando una expresión lambda tiene un solo parámetro, Kotlin lo declara implícitamente bajo el nombre it. Esto acorta el tamaño del código, pero en ocasiones puede ser confuso determinar el contexto al que pertenece, por ejemplo, cuando se tienen lambdas anidadas.

En este ejemplo no ocurre ese caso; sin embargo, en pro de una mejor legibilidad se declaran explícitamente las variables de las lambdas bajo el nombre player.

Para mejorar la lectura del código, las expresiones lambdas se movieron a funciones con nombres significativos como la función isYoung, que será parte de la clase Player, y la función scoreExceedsMinimumValue.

Como estas funciones tienen una sola expresión, Kotlin puede inferir fácilmente su valor de retorno, pudiéndose usar una sintaxis más corta, omitiendo las llaves para obtener funciones más sencillas de leer.

De manera similar, se declara explícitamente la variable de la lambda que se encarga de ordenar los resultados.

Por último, se refactoriza la expresión lambda que realiza la conversión de una instancia de Player a Winner:

Para eliminar los bloques anidados if-else se utiliza la expresión when. El operador in se usa para validar los rangos de los puntajes, obteniendo un código mucho más conciso y simple.

***Notar que para facilitar la explicación se omitió el reemplazo de los números por constantes, pero eso es algo que siempre se debe hacer. ***

2. Utilizando datos inmutables

Es recomendable utilizar objetos inmutables porque vuelve al código más predecible, ya que se eliminan los efectos secundarios. Así nos aseguramos de que los objetos no se modifiquen en lugares inesperados. Esto los vuelve mucho más seguros de usar en aplicaciones concurrentes.

Kotlin ofrece soporte para su implementación a través de sus data class, las cuales representan clases que solo contienen estado. Tienen muchas ventajas de uso. Una de ella es que, mediante las propiedades declaradas en su constructor, se evita todo el boilerplate de los getters y setters.

Por ejemplo:

Las propiedades declaradas con val son inmutables, de manera que ninguna instancia de Player podrá modificarse una vez creada. Para crear una nueva instancia a partir de una ya existente se puede usar la función copy.

Es importante señalar que la función copy no realiza una copia profunda. Por lo tanto, si alguna de las variables no es un tipo de dato primitivo, la inmutabilidad no está garantizada a menos que se use consistentemente.

Después de la copia, la referencia de Player será la misma en ambas instancias de Game. No obstante, dado que las variables de Game son inmutables, queda garantizada la inmutabilidad.

En esta primera entrega explicamos dos de las buenas prácticas más importantes a la hora de lograr “clean code” con Kotlin. Resumiendo,

  • Las clases o funciones pequeñas facilitan la comprensión y vuelven el código más mantenible.
  • Los módulos de código deben tener una única responsabilidad. Esta recomendación forma parte de unos los principios SOLID, de la programación orientada a objetos.
  • La nomenclatura, al momento de nombrar variables, métodos, clases y funciones debe ser representativa para permitir una lectura más fluida.

En una próxima entrega, hablaremos sobre cómo evitar establecer variables con valores nulos y cómo manejar errores para obtener Clean code. Y ustedes, ¿qué otras prácticas de clean code utilizan?

Karlo Ismael Morales Oviedo

Karlo Ismael Morales Oviedo es desarrollador full-stack en intive Argentina desde marzo de 2018. Ingeniero en Informática egresado de la Universidad de Buenos Aires (UBA), Karlo además es técnico en control de máquinas y procesos industriales graduado del Servicio Nacional de Adiestramiento en Trabajo Industrial de Lima, Perú. Entre sus tantos hobbies, destacan: ir al cine, jugar al fútbol (hincha de River) y jugar videojuegos -en especial de acción o aventuras-. Siempre al día con las nuevas tecnologías que le interesan, en un futuro le gustaría realizar una especialización en Inteligencia Artificial.  

Deja un comentario