Código Limpio
¿Quién es el
mejor programador?
Programador Sobresaliente
- ¿Aquel que conoce de memoria
el API de un lenguaje? - ¿Aquel que conoce comportamientos
raros de un lenguaje? - ¿Aquel que conoce muchísimos
frameworks o lenguajes? - ¿Aquel que escribe el
código muy rápido? - ¡¿Aquel que escribe código
que nadie más entiende?!
Mi respuesta
"Alguien que se preocupa por el código que escribe"
Y no hay preocupación
más importante
que hacerlo limpio
¿Qué es
código limpio?
Código Limpio
Código que se puede entender sin tener
a la persona que lo escribió al lado
(puedo ser yo hace unas semanas)
Código Limpio
- Fácil de entender
- Menos propenso a errores
- Fácil de probar
- Fácil de extender
- Fácil de corregir
¡Una empresa puede acabarse, por no
escribir código limpio!
Nombres
Usa nombres expresivos
int d; // días de la semana
int díasDeLaSemana;
NO escribas
Mejor escribe
void copiar(char[] a1, char[] a2)
void copiar(char[] fuente, char[] destino)
NO escribas
Mejor escribe
No uses abreviaturas
int cntrl;
int control;
int central;
NO escribas
Mejor escribe
Usa nombres fáciles
de pronunciar / buscar
Usa constantes en vez
de números mágicos
for (int i = 0; i < 34; ++i) {
s += (t[i] * 4) / 5;
}
final int NÚMERO_TAREAS = 34;
final int NÚMERO_REAL_DÍAS = 4;
final int DÍAS_LABORALES = 5;
for (int i = 0; i < NÚMERO_TAREAS; ++i) {
suma += (tarifa[i] * NÚMERO_REAL_DÍAS) / DÍAS_LABORALES;
}
NO escribas
Mejor escribe
Comentarios
Antes nos parecía
muy importante
escribir comentarios
Ahora sabemos que es
muy frecuente que el código y el comentario
no estén de acuerdo
// calculamos el mínimo común múltiplo de a y b
int mcm = a * b;
¡El código es la
mejor documentación!
Criba de Eratóstenes
- Supongamos una lista infinita
de números naturales - Tachamos los números
que obviamente no son primos - Nos paramos en el primer número
no recorrido que no haya sido tachado
y lo encerramos en un círculo
indicando que son primos - Luego tachamos todos los
múltiplos de ese número. - Repetimos los 2 últimos pasos
Criba de Eratóstenes
public class CribaDeEratóstenes {
private static final int MAX_PRIMO = 1_000_000;
private boolean[] primo = new boolean[MAX_PRIMO + 1];
public CribaDeEratóstenes() {
Arrays.fill(primo, true);
primo[0] = false;
primo[1] = false;
for (int i = 2; i * i <= MAX_PRIMO; ++i) {
if (primo[i]) {
for (int j = i * i; j <= MAX_PRIMO; j += i) {
primo[j] = false;
}
}
}
}
public boolean isPrime(int n) {
return primo[n];
}
}
¿Podemos hacerlo
más legible?
public class CribaDeEratóstenes {
private static final int MAX_PRIMO = 1_000_000;
private boolean[] primo = new boolean[MAX_PRIMO + 1];
public CribaDeEratóstenes() {
destacharTodosLosNúmeros();
tacharNúmerosObvios();
tacharMúltiplosDePrimosConocidos();
}
public boolean esPrimo(int n) {
return noEstáTachado(n);
}
// más código
}
private void tacharNúmerosObvios() {
tachar(0);
tachar(1);
}
private void tacharMúltiplosDePrimosConocidos() {
for (int n = 2;
menorMúltiploPosiblementeNoTachado(n) <= MAX_PRIMO; ++n) {
if (esPrimo(n)) {
tacharMúltiplosDe(n);
}
}
}
private void tacharMúltiplosDe(int n) {
for (int x = menorMúltiploPosiblementeNoTachado(n);
x <= MAX_PRIMO; x = siguienteMúltiplo(x, n)) {
tachar(x);
}
}
private static int menorMúltiploPosiblementeNoTachado(int n) {
return n * n;
}
private static int siguienteMúltiplo(int x, int n) {
return x + n;
}
Hasta aquí, todos
los métodos están
escritos como prosa
Y por último los métodos de más baja abstracción
los que revelan detalles
de implementación
private void destacharTodosLosNúmeros() {
Arrays.fill(primo, true);
}
private void tachar(int n) {
primo[n] = false;
}
private boolean noEstáTachado(int n) {
return primo[n];
}
Indirección
Introducimos métodos,
con buenos nombres,
para agregar un grado
de indirección
Un método permite ponerle un nombre a un conjunto de instrucciones. Que además ayude a que sean reutilizadas es incidental
Métodos
Métodos
- Cortos
- Hacer solo una cosa
- No tener repetición
Métodos Cortos
- Entre 5 y 10 líneas
- Más que líneas piensa en responsabilidades
- Recuerda los grados de indirección
¡Esta es rara vez la manera en que codificamos!
public static boolean solucionar(int [][]tablero) {
int x=0,y=0;
boolean encontrado = false;
for(x = 0;x < 9; x ++) {
for(y = 0;y < 9; y++) {
if(tablero[x][y] == 0) {
encontrado = true;
break;
}
}
if( encontrado ) break;
}
if(!encontrado) return true;
boolean digitos[] = new boolean[11];
for(int i = 0; i < 9; i++) {
digitos[tablero[x][i]] = true;
digitos[tablero[i][y]] = true;
}
int bx = 3 * (x/3), by = 3 * (y/3);
for(int i =0;i<3;i++)
for(int j = 0; j < 3; j++)
digitos[tablero[bx+i][by+j]] = true;
for(int i = 1 ; i <= 9; i++) {
if(!digitos[i] ) {
tablero[x][y] = i;
if(solucionar(tablero))
return true;
tablero[x][y] = 0;
}
}
return false;
}
Tomado de Internet
Saquemos responsabilidades puntuales a nuevos métodos
Pero primero introduzcamos
Tipos de Datos
public class Sudoku {
private int[][] tablero;
public Sudoku(int[][] tablero) {
this.tablero = tablero;
}
private class Coordenada {
int hilera;
int columna;
boolean vacía;
private Coordenada(int hilera, int columna, boolean vacía) {
this.hilera = hilera;
this.columna = columna;
this.vacía = vacía;
}
Coordenada(int hilera, int columna) {
this(hilera, columna, false);
}
Coordenada() {
this(-1, -1, true);
}
}
// más código
}
Ahora saquemos responsabilidades a
nuevos métodos
int x=0,y=0;
boolean encontrado = false;
for(x = 0;x < 9; x ++) {
for(y = 0;y < 9; y++) {
if(tablero[x][y] == 0) {
encontrado = true;
break;
}
}
if( encontrado ) break;
}
if(!encontrado) return true;
Antes
private Coordenada buscarSiguienteCoordenada() {
for (int i = 0; i < 9; ++i) {
for (int j = 0; j < 9; ++j) {
if (tablero[i][j] == 0) {
return new Coordenada(i, j);
}
}
}
return new Coordenada();
}
Después
int x = celda.fila;
int y = celda.columna;
boolean digitos[] = new boolean[11];
for(int i = 0; i < 9; i++) {
digitos[tablero[x][i]] = true;
digitos[tablero[i][y]] = true;
}
int bx = 3 * (x/3), by = 3 * (y/3);
for(int i =0;i<3;i++)
for(int j = 0; j < 3; j++)
digitos[tablero[bx+i][by+j]] = true;
Antes
private List<Integer> digitosPosibles(Coordenada coordenada) {
Set<Integer> usados = new HashSet<>();
usados.addAll(usadosHorizontalmente(coordenada));
usados.addAll(usadosVerticalmente(coordenada));
usados.addAll(usadosEnCuadrante(coordenada));
List<Integer> dígitos =
new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9));
dígitos.removeAll(usados);
return dígitos;
}
Después
private Set<Integer> usadosHorizontalmente(Coordenada coordenada) {
Set<Integer> usados = new HashSet<>();
for (int i = 0; i < tablero.length; ++i) {
usados.add(tablero[coordenada.hilera][i]);
}
return usados;
}
private Set<Integer> usadosVerticalmente(Coordenada coordenada) {
Set<Integer> usados = new HashSet<>();
for (int i = 0; i < tablero.length; ++i) {
usados.add(tablero[i][coordenada.columna]);
}
return usados;
}
private Set<Integer> usadosEnCuadrante(Coordenada celda) {
Set<Integer> usados = new HashSet<>();
int x = 3 * (celda.hilera / 3);
int y = 3 * (celda.columna / 3);
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
usados.add(tablero[x + i][y + i]);
}
}
return usados;
}
Después
private boolean backtrack() {
Coordenada siguiente = buscarSiguienteCoordenada();
if (siguiente.vacía) {
return true;
}
List<Integer> dígitos = dígitosPosibles(siguiente);
for (int n : dígitos) {
tablero[siguiente.hilera][siguiente.columna] = n;
if (backtrack()) {
return true;
}
tablero[siguiente.hilera][siguiente.columna] = 0;
}
return false;
}
Tipos de Datos
- Hacen el código más legible
- Ayudan a reducir la
lista de parámetros - Hacen que ciertos
errores sean imposibles
public Circle(double x, double y, double radius)
public Circle(Punto centro, double radius)
En vez de
Mejor
public void execute(String query)
public void execute(Query query)
En vez de
Mejor
La mejor forma de hacer el código más legible, es escribir más métodos
Críticas al
Código Limpio
Críticas
- "Es escriben más métodos,
lo que impacta el desempeño" - "Los métodos de una sola
línea son un exabruptos" - "¿Para entender más el código,
tengo que leer más código?"
Es cierto que escribir código limpio tiene un costo, pero no es tan alto como se piensa
Legibilidad es mucho
más importante
que Desempeño
¿Cómo escribir código limpio?
Escribir Código
- Haz que el código funcione
- Haz que el código sea limpio
- Haz que el código sea rápido
¡Gracias!
@gaijinco
https://slides.com/gaijinco/codigo-limpio-itc
Q&A
Código Limpio
By Carlos Obregón
Código Limpio
- 1,594