Dragon Ball Clean Code - Añadir contexto con sentido
Esta es una de las recomendaciones más importartes de Uncle Bob, en lo que a nomenclatura se refiere.
Comienza la aventura!
Goku y Krilín están entrenando con el maestro Muten, como sabréis no sólo les enseñaba artes marciales, también les instruía en letras y ciencias y les hacía trabajar repartiendo leche o ayudando en la construcción.
Hoy toca clase de programación, Krilín que es un coco (nunca mejor dicho) está encantado, pero a Goku la tecnología nunca se le ha dado bien y mucho menos la algoritmia.
Muten les encargó un programa que sirviera para catalogar todas las revistas, de adultos, que colecciona el maestro.
Goku está programando un widget que indica el número de revistas seleccionado de una lista, es algo bastante sencillo, al final es mostrar un texto que diga si hay “X revistas seleccionadas”.
Después de gastar todo el Backlog Refinement explicando a Goku que si tienes CERO elementos de un ítem el mensaje debe hablar del número de ítems en plural, puedo comenzar la tarea.
Goku acabó y estamos Krilin va a hacerle un Code Review y se encuentra la siguiente función:
private String getWidgetMessage(ArrayList<Magazine> magazines) {
Integer numberOfMagazines = magazines.size();
String numberOfMagazinesString = "0";
String isOrAre = "is"; // 'is' by default
Boolean isZeroOrMoreThanOne = true;
String magazine_s = "magazine"; // add an 's' if plural
if (numberOfMagazines == 0) {
numberOfMagazinesString = numberOfMagazines + "";
isOrAre = "are";
isZeroOrMoreThanOne = true;
} else if (numberOfMagazines == 1) {
numberOfMagazinesString = numberOfMagazines.toString();
isOrAre = "is";
isZeroOrMoreThanOne = false;
} else {
numberOfMagazinesString = String.parse(numberOfMagazines);
isOrAre = "are";
isZeroOrMoreThanOne = false;
}
if (isZeroOrMoreThanOne) {
magazine_s = magazine_s + "s";
}
return "There" + " " + isOrAre + " " + numberOfMagazinesString + " " + magazine_s;
}
Sí, a Krilín también le sangraban los ojos al ver el algoritmo. En realidad no estaba seguro de que aquello compilase, pero era la menor de sus preocupaciones, debía intruir en buenas prácticas escribiendo código, si no el mantenimiento de la aplicación sería un infierno a largo plazo (incluso a corto)
- “Goku”, dijo Krilín.
- “Vamos a hacer una clase que contenga esta lógico para darle un contexto común”, continuó.
- “Y luego lo desgranamos en funciones pequeñas que le den significado a los pasos del algoritmo”.
Goku lo miraba extrañado, en su PC funcionaba, esta gente de ciuidad y con sus Macs, como Krilín, eran muy exquisitos con todo.
public class WidgetMessage {
String verb;
String quantity;
String pluralTermination;
Integer numberOfSelectedItems;
public String buildMessageForSelectedItemsOnList(ArrayList<?> items, String itemType) {
numberOfSelectedItems = countSelectedItems(items);
fillPluralDependentParts(numberOfSelectedItems);
return String.format(
"There %s %s %s%s",
verb, quantity, itemType, pluralTermination);
}
private Integer countSelectedItems(ArrayList<?> items) {
Integer numberOfSelectedItems = 0;
if (items != null) {
numberOfSelectedItems = items.FindAll(item => item.isSelected).size();
}
return numberOfSelectedItems;
}
private void fillPluralDependentParts(Integer numberOfSelectedItems) {
if (numberOfSelectedItems == 0) {
fillPartsWhenThereAreNoItems();
} else if (numberOfSelectedItems == 1) {
fillPartsWhenThereIsOneItem();
} else {
fillPartsWhenThereAreManyItems();
}
}
private void fillPartsWhenThereAreNoItems() {
verb = "is";
quantity = "no";
pluralTermination = "s";
}
private void fillPartsWhenThereIsOneItem() {
verb = "is";
quantity = "1";
pluralTermination = "";
}
private void fillPartsWhenThereAreManyItems() {
verb = "are";
quantity = numberOfSelectedItems.toString();
pluralTermination = "s";
}
}
Goku miraba a Krilín extrañado, su solución tenía menos líneas y hacía lo mismo ¿por qué a solución de Krilín era mejor?
- “Goku”, dijo Krilín
- “Cuanto has tardado antes en explicarme tu código?”
Goku no lo recordaba pero Krilín, que es un psicópata de la productividad ll había medido, de hecho estaba grabando la sesión de Code Review para subirla a su canal de Diostube. Sacó parte del vídeo y le puso a Goku su propia explicación:
Mira Krilín, he hecho esto:
private String getWidgetMessage(ArrayList<Magazine> magazines) {
Integer numberOfMagazines = magazines.size();
String numberOfMagazinesString = "0";
String isOrAre = "is"; // 'is' by default
Boolean isZeroOrMoreThanOne = true;
String magazine_s = "magazine"; // add an 's' if plural
if (numberOfMagazines == 0) {
numberOfMagazinesString = numberOfMagazines + "";
isOrAre = "are";
isZeroOrMoreThanOne = true;
} else if (numberOfMagazines == 1) {
numberOfMagazinesString = numberOfMagazines.toString();
isOrAre = "is";
isZeroOrMoreThanOne = false;
} else {
numberOfMagazinesString = String.parse(numberOfMagazines);
isOrAre = "are";
isZeroOrMoreThanOne = false;
}
if (isZeroOrMoreThanOne) {
magazine_s = magazine_s + "s";
}
return "There" + " " + isOrAre + " " + numberOfMagazinesString + " " + magazine_s;
}
- G: “Mira Krilín, he hecho esta función tó chula que hace un mensaje para el número de revistas seleccionadas”.
- G: “Primero calculo el número de revistas seleccionadas”
- K: “Ah, ya te tiene que venir la lista filtrada?”
- G: “Er.. sí.”
- K: “Es que por el nombre me pareció que eran todas las revistas, y veo que esto sólo sirve para revistas, no?”
- G: “Sí, claro! Es lo que había que hacer no?”
- K: “Bueno, sí, pero ya sabes que el Maestro Muten tiene también vídeos, CDs, figuras… quizás podría querer controlar toda su colección.”
- G: “Bueno, si lo quiere que lo pida ya lo ajustaremos.”
- G: “Todo se gestiona en estas tres variables: numberOfMagazinesString,…”
- K: “Y ahí guardas un número o una String?”
- G: “Una String, no lo ves? Si te lo dice el IDE!”
- K: “Es verdad, ya te lo dice el IDE, para qué le pones String al nombre de la variable entonces?”
- G: “Hoy estás puñetero, eh?”
- K: “Sigue, disculpa.”
- G: “Pues eso, tres variables:
numberOfMagazinesString
,isOrAre
yisZeroOrMoreThanOne
” - K: “Espera que no me aclaro, debo estar espeso, ¿qué es isOrAre? ¿qué evalúa? ¿qué es ‘orAre’?
- G: “No evalúa nada, no ves que es otra String? Es una String que puede guardar el valor ‘is’ o el valor ‘are’, que es el verbo que utilizaré para montar la frase. Sí que estás espeso hoy, si hasta tiene un comentario explicativo!”
- K: “Vale, es que como después está la variable
isZeroOrMoreThanOne
que sí entiendo que evalúa si algo es cero o mayor que uno.” - G: “Ves? Código autoexplicativo como siempre pide el Maestro Muten”.
- K: “… vale, continúa.”
- G: “Oh, y esto es lo mejor, en
magazine_s
guardo el valor en singular o plural de la palabra ‘magazine’, no es ingenioso este nombre de variable?” - K: “No estábamos respetando la notación CamelCase?”
- G: “La qué?”
- K: “Luego te lo explico, sigamos…”
- G: “El resto es bastante del tirón, si hay cero revistas en
numberOfMagazinesString
pondré el número convertido a String, sabía que al concatenar un número con una String sale una String?” - K: “Sí…”
- G: “Pongo el verbo en plural como me dijísteis”
- K: “Bien…”
- G: “Y a
isZeroOrMoreThanOne
le pongo true porque es cero” - K: “Vale y qué más?”
- G: “Bueno, pues hago lo propio si hay una revista o más de una, no lo ves?”
- K: “Bueno… podría intuírlo porque es una función pequeña, pero entre que algunos nombres de variables me confunden y veo que dentro de cada if hay unas transformaciones de tipo, pues no estoy tan seguro de que haga lo mismo, al menos hay más riesgo de que cada if se comporte distinto…”
- G: “No hombre, no! Esto está probado!”
- K: “Ah sí? Tienes tests? Me dejas ver?”
- G: “Bueno, ya tu sabes, probado a mano, luego si da tiempo los hago”
- K: “Vaya… bueno, si no te importa acaba de explicarme el algoritmo”
- G: “Bueno, lo que decías que es distinto, pues nada, sólo convierto a String el número de revistas de diferentes maneras, así el código es más entretenido de leer.”
- K: “Más complejo de leer, dirás, hay que mirar al detalle a ver si lo que hace es correcto o no, es poco intuitivo…”
- G: “Yo lo entiendo súper fácil”
- K: “Porque lo has hecho tú y lo has acabado de hacer, si tienes que volver a este código la semana que viene, y nos conocemos y la memoria no es lo tuyo, te lo vas a tener que volver a leer de arriba a abajo”
- G: “Esto no falla, no habrá que volver sobre este código nunca!”
- K: “Y si el Maestro quiere inventariar DVDs?”
- G: “Pues…”
Krilín pausó el vídeo.
“Goku”, ahora lee mi código y explícamelo.
- G: “A ver… esto de explicar código de otra persona es más complicado…”
public class WidgetMessage {
String verb;
String quantity;
String pluralTermination;
Integer numberOfSelectedItems;
public String buildMessageForSelectedItemsOnList(ArrayList<?> items, String itemType) {
numberOfSelectedItems = countSelectedItems(items);
fillPluralDependentParts(numberOfSelectedItems);
return String.format(
"There %s %s %s%s",
verb, quantity, itemType, pluralTermination);
}
private Integer countSelectedItems(ArrayList<?> items) {
Integer numberOfSelectedItems = 0;
if (items != null) {
numberOfSelectedItems = items.FindAll(item => item.isSelected).size();
}
return numberOfSelectedItems;
}
private void fillPluralDependentParts(Integer numberOfSelectedItems) {
if (numberOfSelectedItems == 0) {
fillPartsWhenThereAreNoItems();
} else if (numberOfSelectedItems == 1) {
fillPartsWhenThereIsOneItem();
} else {
fillPartsWhenThereAreManyItems();
}
}
private void fillPartsWhenThereAreNoItems() {
verb = "is";
quantity = "no";
pluralTermination = "s";
}
private void fillPartsWhenThereIsOneItem() {
verb = "is";
quantity = "1";
pluralTermination = "";
}
private void fillPartsWhenThereAreManyItems() {
verb = "are";
quantity = numberOfSelectedItems.toString();
pluralTermination = "s";
}
}
- G: “Hay una clase que gestiona el mensaje del Widget”.
- K: “Correcto, todo lo que ocurre dentro de esta clase es para gestionar ese contexto, la gestión del texto del widget”
- K: “Tu función donde estaba?”
- G: “Bueno, en esta clase
BastónMágico
” - K: “Pero esta clase tiene 19000 líneas, qué hace?”
- G: “Pues de todo, a que es fuerte? Además hace honor a su nombre, quiero hacerla lo más larga posible a ver si no tiene límite como mi bastón!” (En realidad Krilín no le escuchaba, todavía estaba procupado por esa clase con tíldes en su nombre)
- K: “Con… continúa”
- G: “Vale pues entiendo que empieza por la función
buildMessageForSelectedItemsOnList
que construye el mensaje.” - G: “Qué función más corta, no? Ya lo hace todo? A ver…”
- G: “Cuenta los items, rellena los textos y devuelve el mensaje.”
- K: “Tal cual”
- G: “Ok sigamos”
- K: “Hace falta? el resto es entrar al detalle de implementación”
- G: “Te la explico o no?”
- K: “Sigue”
- G: “Según los items rellenas las partes de String para cada caso y cubres el cero, el uno y el más de uno”
- K: “Exactamente”
- G: “Segumimos”
- K: “Si quieres ir más al detalle… pero te das cuenta que leyendo sólo el camino ya te explica que hace?”
- G: “Sí… no está mal…”
Y fin de la aventura!!!
A Goku se le da bien dar mamporros, suerte que para pensar ya tenemos a Bulma, no?
Este post ha sido bastante más denso que los anteriores, y es que con este cerramos el Capítulo “NOMBRES CON SENTIDO” del libro.
Quería tratar de viajar a través de la mente de 2 desarrolladores que se leen mútuamente el código y “forzaros” a vivir una experiencia de code review que quizás no habéis tenido oportunidad de experimentar o que quizás sois más juniors y no os veis capaces de coger el rol de Krilín o no os podéis imaginar la de cosas que se le pasan por la cabeza a un senior cuando os revisa el código.
Hay que decir que Krilín ha sido bastante benevolente, bien por Krilín!
A veces no nos damos cuenta y no somos respetuosos con el código ajeno y eso vale más que el código más limpio.
Siempre se puede hacer mejor, incluso al código de Uncle Bob se le puede dar otra vuelta, por eso hemos de respetar que cada uno lo hace mejor que puede con las herramientas y conocimiento que tiene en ese momento y entre todos debemos ayudarnos a mejorar y caminar juntos hacia el siguiente paso de mejora.
Gracias por aguantar la chapa!
Agradecimientos
Gracias a mis compañeros y compañeras de Basetis por el acceso a este libro y la flexibilidad para escribir este contenido que comparto con vosotras.