01.03 - Principios de Código Limpio
Lenguajes Estructurados
Agenda
- Introducción a las buenas prácticas
- Principio DRY (Don't Repeat Yourself)
- Principio ETC (Easy to Change)
- Ejemplos y aplicaciones
- Conclusiones y consejos finales
Introducción a las buenas prácticas
- Las buenas prácticas son hábitos y técnicas que facilitan el desarrollo de software.
- Ayudan a crear código más legible, mantenible y eficiente.
- Dos principios importantes son DRY y ETC.
Una cosa está bien diseñada si se adapta a las personas que la utilizan.
Para el código, eso significa que debe adaptarse cambiando, así que creemos en el principio ETC: Easier to Change
, más fácil de cambiar.
ETC es la base fundamental. Cualquier principio de diseño que existe puede considerarse un caso especial del ETC.
¿Por qué es bueno el desacoplamiento? Porque al aislar los intereses hacemos que sea más fácil cambiar. ETC.
¿Por qué es útil el principio de responsabilidad única? Porque un cambio en los requisitos se corresponde con un cambio en un solo módulo. ETC.
¿Por qué es importante el momento de poner nombres? Porque los buenos nombres hacen que el código sea más fácil de leer, y hay que leerlo para cambiarlo. ¡ETC!
ETC es un valor, no una regla
Los valores son cosas que nos ayudan a tomar decisiones: ¿debería hacer esto o aquello?
A la hora de pensar en el software, ETC es una guía que nos ayuda a elegir entre varios caminos.
Al igual que todos los demás valores, debería estar siempre presente en el pensamiento consciente, empujándonos de manera sutil en la dirección correcta.
Hay una premisa implícita en ETC
Asume que una persona puede decir cuál entre muchos caminos de implementación será más fácil de cambiar en el futuro.
A veces no es tan evidente esta decisión. En esos casos, se pueden hacer dos cosas:
- Primero, dado que no estamos seguros de qué forma adoptará el cambio, siempre podemos ir por el camino definitivo del "fácil de cambiar"
- intentar hacer que lo que escriba sea sustituible
- De ese modo, pase lo que pase en el futuro, esta porción de código no será un obstáculo en el camino
- Se trata solo de pensar en mantener el código desacoplado y cohesivo.
- Segundo, considar esto como una manera de desarrollar el instinto.
- Tomar nota de la situación, las opciones que tiene y algunas conjeturas sobre el cambio.
- Dejar una etiqueta en el código fuente.
- Más adelante, cuando este código tenga que cambiar, podremos echar la vista atrás y encontrar feedback de nosotros mismos.
- Además, puede que esto ayude la próxima vez que nos encuentremos con una bifurcación similar en el camino.
DRY: los males de la duplicación
Cuando llevamos a cabo el mantenimiento, tenemos que encontrar y cambiar las representaciones de las cosas, esas cápsulas de información incrustadas en la aplicación.
El problema es que es fácil duplicar la información en las especificaciones, procesos y programas que desarrollamos y, cuando lo hacemos, damos lugar a una pesadilla de mantenimiento, una que empieza mucho antes de que se envíe la aplicación.
La única manera de desarrollar software de manera fiable, y de hacer que nuestros desarrollos sean más fáciles de entender y mantener, es seguir lo que llamamos principio DRY.
Cualquier porción de información debe tener una representación única, inequívoca y autoritativa dentro de un sistema.
La alternativa es tener la misma cosa expresada en dos o más lugares.
Si se cambia una, hay que recordar cambiar las otras o el programa terminará fallando por sus propias contradicciones.
No es una cuestión de si nos vamos a acordar : es una cuestión de cuándo nos vamos a olvidar.
DRY es más que código
DRY tiene que ver con la duplicación de la información, del propósito.
Tiene que ver con expresar la misma cosa en dos lugares diferentes, posiblemente de dos maneras distintas por completo.
Cuando tenemos que cambiar un solo aspecto del código:
- ¿ descubrimos que tenemos que hacer ese cambio en múltiples lugares y en múltiples formatos diferentes?
- ¿Tenemos que cambiar el código y la documentación, o un esquema de base de datos y una estructura que lo alberga, o..?
Si es así ese código no es DRY
Resumen
Principio DRY (Don't Repeat Yourself)
- El principio DRY se enfoca en reducir la duplicación de código.
- Asegura que cada parte del conocimiento tiene una representación única y bien definida dentro del sistema.
- Evita la duplicación de esfuerzos y hace que el mantenimiento sea más sencillo.
Principio ETC (Easy to Change)
- El principio ETC busca que el código sea fácil de modificar.
- Fomenta la modularidad, la desacoplamiento y la responsabilidad única.
- Facilita la adaptación a cambios en los requisitos y la corrección de errores.
Ejemplos y aplicaciones
DRY
- Utilizar funciones y clases para encapsular comportamientos comunes.
- Utilizar constantes y variables para evitar valores "mágicos".
ETC
- Diseñar interfaces claras y simples entre módulos.
- Utilizar patrones de diseño para facilitar la extensibilidad.
Ejemplo de aplicación de DRY
Supongamos que tenemos el siguiente código:
int imprimir_balance(cuenta_t *cuenta) {
printf("Debitos: %10.2f \n", cuenta->debitos);
printf("Creditos: %10.2f \n", cuenta->creditos);
if (cuenta->tarifas < 0) {
printf ("Tarifas: %10.2f-\n", (-1)*cuenta->tarifas)
} else {
printf ("Tarifas: %10.2f+\n", cuenta->tarifas)
}
printf(" ---------------------\n");
if (cuenta->balance < 0) {
printf ("Balance: %10.2f-\n", (-1)*cuenta->balance)
} else {
printf ("Balance: %10.2f+\n", cuenta->balance)
}
return 0;
}
Identificar la duplicación de código y refactorizarlo para evitarla
En primer lugar, hay una duplicación clara de tipo cortar y pegar de la gestión de los números negativos. Podemos arreglarlo añadiendo otra función:
void imprimir_numero(double numero) {
if (numero < 0) {
printf("%10.2f-\n", (-1)*numero);
} else {
printf("%10.2f+\n", numero);
}
}
imprimir_balance(cuenta_t *cuenta) {
printf("Debitos: ");
imprimir_numero(cuenta->debitos);
printf("Creditos: ");
imprimir_numero(cuenta->creditos);
printf("Tarifas: ");
imprimir_numero(cuenta->tarifas);
printf(" ---------------------\n");
printf("Balance: ");
imprimir_numero(cuenta->balance);
return 0;
}
Otra duplicación es la repetición del formato de impresión del campo en todas las llamadas printf.
Podríamos arreglar esto introduciendo una constante y pasándola a cada llamada.
FORMAT_STRING = "%10.2f"
Pero también se puede evitar la duplicación refactorizando la propia función existente.
void imprimir_numero(double numero, char *buffer) {
sprintf(buffer, "%10.2f", numero);
if (numero < 0) {
sprintf(buffer, "%s-", buffer);
} else {
sprintf(buffer, "%s+", buffer);
}
}
imprimir_balance(cuenta_t *cuenta) {
char buffer[20];
printf("Debitos: %s\n", imprimir_numero(cuenta->debitos, buffer));
printf("Creditos: %s\n", imprimir_numero(cuenta->creditos, buffer));
printf("Tarifas: %s\n", imprimir_numero(cuenta->tarifas, buffer));
printf(" ---------------------\n");
printf("Balance: %s\n", imprimir_numero(cuenta->balance, buffer));
return 0;
}
¿Algo más? ¿qué pasa si el cliente pide un espacio extra entre las etiquetas y los números?
Tendríamos que cambiar cinco líneas.
Podemos eliminar también esa duplicación:
void imprimir_numero(double numero, char *buffer) {
sprintf(buffer, "%10.2f", numero);
if (numero < 0) {
sprintf(buffer, "%s-", buffer);
} else {
sprintf(buffer, "%s+", buffer);
}
}
void imprimir_etiqueta(char *etiqueta) {
printf("%s: ", etiqueta);
}
void imprimir_linea( char *etiqueta, double numero, char *buffer) {
imprimir_etiqueta(etiqueta);
imprimir_numero(numero, buffer);
}
imprimir_balance(cuenta_t *cuenta) {
char buffer[20];
imprimir_linea("Debitos", cuenta->debitos, buffer);
imprimir_linea("Creditos", cuenta->creditos, buffer);
imprimir_linea("Tarifas", cuenta->tarifas, buffer);
printf(" ---------------------\n");
imprimir_linea("Balance", cuenta->balance, buffer);
return 0;
}