Indentación
del código fuente
Todos
los desarrolladores utilizan algún estilo de
colocación de los elementos del código fuente
dentro del texto que los contiene. Esto se define como el estilo de
codificación. Las normas de indentación indican
la posición en la que se deben colocar los diferentes
elementos que se incluyen en el código fuente, por lo que
forman parte del estilo de codificación. Otro ejemplo de
ello es la separación con espacios en blanco entre los
diferentes elementos que componen las líneas de
código.
Objetivo de la
indentación
El objetivo fundamental de la
indentación del código fuente es facilitar su
lectura y comprensión. Hay dos tipos de posibles lectores
del código fuente: programas y personas. A los programas les
da igual la indentación, leen bien nuestro código
siempre que cumpla la sintaxis del lenguaje. Luego la
indentación debe centrarse en la lectura y
comprensión del código por personas.
Para
entender cómo hay que indentar un código primero
hay que entender cómo lo lee una persona. Actualmente la
mayor parte de las personas que leen código fuente
lo hacen con editores de texto simples o con la ayuda de los editores
de los entornos de desarrollo. Por tanto nos vamos a centrar en este
tipo de lectores.
Cuando una persona lee
un código fuente, lo hace siguiendo una serie de patrones de
lectura. Si queremos facilitar la lectura del código lo
primero que hay que entender son estos patrones. La mejor
bibliografía sobre patrones de lectura son los libros que
enseñan sistemas de lectura rápida; dos puntos
fundamentales de estos sistemas son el estudio de los patrones de
movimiento de la vista y el reconocimiento de la estructura del texto.
Los
patrones de lectura nos indican el movimiento que siguen los ojos al
leer un texto. Cuando leemos un texto, la vista se desliza a saltos, no
se desplaza de una manera continua. Nuestra vista capta la
información y que haya alrededor del punto en que se detiene
la vista. Un buen lector hace pocas paradas para leer una
línea, aprovechando más su campo visual.
Por
otra parte, al leer también utilizamos la visión
periférica, aprovechando la estructura y
disposición que tienen las letras para extraer
información adicional del texto. Así, por ejemplo,
instintivamente nos fijamos en el principio del párrafo para
buscar la información relevante, y nos ayudamos de elementos
como las listas, los tabuladores, tablas y otros elementos
tipográficos al leer.
De estos puntos
deducimos que la posición absoluta y relativa de los
elementos que componen el texto del código fuente juegan un
papel importante a la hora de facilitar la lectura y
comprensión del mismo.
Requerimientos
para definir un estilo de indentación
Vamos
a enumerar los principales requisitos para poder definir un buen estilo
de indentación. Algunos de los más importantes
serán:
- Que el
posicionamiento de los elementos dentro del texto sea predecible.
Cuanto más predecible es la posición, el
movimiento de los ojos llega antes a los puntos relevantes del
código fuente. El punto más relevante de
información es siempre el principio de la línea,
por lo que hay que tener especial atención en él. - Que
la relevancia de la agrupación de los elementos refleje la
relevancia de éstos dentro del programa. - Priorizar
la identificación del flujo del programa frente al detalle
de los elementos que lo componen. Por ejemplo es más
importante siempre tener una visión global de lo que hace
una función que del detalle de cómo se declaran
sus variables. - Que los comentarios respeten la
indentación de los elementos a los que van asociados,
posicionándose de manera relativa a ellos según
su relevancia. Si el lenguaje lo permite, hay que distinguir la
relevancia de un comentario posicionándolo sobre el elemento
(más importante) o al lado de él (menos
importante). Es mejor situar los comentarios antes del elemento, ya que
nos sirven de introducción a lo que va a
continuación, y muchas veces nos permitirá
saltarnos un bloque de código que no nos interesa. - Utilizar
la indentación basada en tabuladores. La
indentación basada en el uso de caracteres de espacio en
blanco es menos flexible que la basada en tabuladores, ya que los
editores y entornos de desarrollo modernos permiten adapatar con
facilidad el ancho de los tabuladores al gusto de cada programador. - Mantener
el tamaño de las líneas dentro de los
márgenes de la pantalla, siempre que sea posible. Debemos
tener en cuenta al lector potencial del código fuente. No se
debe pensar que el lector tiene una pantalla demasiado
pequeña o grande, sino asumir que el lector tiene una
pantalla típica dentro del entorno de desarrollo habitual.
Por ejemplo actualmente los desarrolladores suelen tener pantallas
capaces de mostrar 40 líneas y 120 columnas de caracteres
con facilidad. En este caso, utilizar el antiguo margen de 80 columnas
por línea y 25 líneas por pantalla es
más bien escaso. - Utilizar el menor
número de normas de indentación requerido.
Indentar elementos en exceso complica visualmente la lectura del
código, si no son estrictamente necesarios.
Un
ejemplo de indentaciones habituales para C++ y Java
En
primer lugar vamos a examinar diferentes estilos de
indentación de una función C++ con
algunas sentencias if,
for
y llamadas a otras funciones.
void foo(int entrada) { int i=0; if(i===0) { int result = obtener_resultado(“clase”, “tipo”, “valor”); for(int j=1;j<10;++j) { string s = cargar(“dato inicial”, “dato final”, Trae CARGAR_DE_BD); cout << “j vale” << j << endl; } } else { Cout << “no entró” << endl; } } |
Este
estilo está muy extendido entre los programadores, y es el
obligatorio para muchos proyectos. Analicemos un poco su estructura.
Para ello vamos a resaltar algunos elementos.
void foo(int entrada) { |int i=0; |if(i===0) |{ |int result = obtener_resultado(“clase”, “tipo”, “valor”); for(int j=1;j<10;++j) |{ |string s = cargar(“dato inicial”, “dato final”, Trae CARGAR_DE_BD); |cout << “j vale” << j << endl; |} |} else |{ |cout << “no entró” << endl; |} } |
He
introducido símbolos ‘|’ para que se vean los
diferentes niveles de indentación. Además he
resaltado en amarillo el texto que corresponde a la lista de
parámetros pasados las funciones. Veamos qué
requisitos no cumple. En primer lugar, la vista debe
dar muchos saltos innecesarios. La vista va a saltar al menos a los
elementos remarcados. Además algunos de los saltos no siguen
un patrón definido, ya que las distancias entre saltos
varían para el mismo tipo de sentencia.
Mejoremos
este ejemplo un poco. Lo primero es reorganizar las llamadas a
función, ya que son menos importantes que la estructura del
flujo de la función.
void foo(int entrada) { |int i=0; |if(i===0) |{ |int result = obtener_resultado(“clase”, “tipo”, valor”); for(int j=1;j<10;++j) |{ |string s = cargar(“dato inicial”, “dato final”, Trae, CARGAR_DE_BD); |cout << “j vale” << j << endl; |} |} else |{ |cout << “no entró” << endl; |} } |
Los
parámetros de la función no son ahora tan
visibles. La vista ya no va directamente a ellos al intentar seguir el
flujo, tenemos que detenernos en ellos a propósito.
Ahora
modificamos las llaves de apertura y corregimos el tamaño de
los indentados: un tabulador para un nivel interior y dos tabuladores
para escribir la parte de una sentencia que no nos cabe en una
línea.
void foo(int entrada) { |int i=0; |if(i===0) |{ |int result = obtener_resultado(“clase”, “tipo”, valor”); for(int j=1;j<10;++j) { |string s = cargar(“dato inicial”, “dato final”, Trae, CARGAR_DE_BD); |cout << “j vale” << j << endl; |} |} else { |cout << “no entró” << endl; |} } |
Como vemos
la
vista sigue ahora un
patrón predecible. Pero todavía lo podemos
mejorar más. Vamos a colocar las llaves de cierre en una
posición poco usual. Las vamos a poner a la misma altura que
las sentencias que engloban. Veréis el resultado.
void foo(int entrada) { |int i=0; |if(i===0) |{ |int result = obtener_resultado(“clase”, “tipo”, valor”); for(int j=1;j<10;++j) { |string s = cargar(“dato inicial”, “dato final”, Trae, CARGAR_DE_BD); |cout << “j vale” << j << endl; |} |} |else { |cout << “no entró” << endl; |} } |
Como
vemos, las llaves de cierre forman columna con la sentencias a las que
pertenecen. Si añadimos algunas sentencias más lo
apreciareis mejor:
void foo(int entrada) { |int i=0; |if(i===0) |{ |int result = obtener_resultado(“clase”, “tipo”, valor”); for(int j=1;j<10;++j) { |string s = cargar(“dato inicial”, “dato final”, Trae, CARGAR_DE_BD); |cout << “j vale” << j << endl; |} |llamadaAotraFuncion(parametro1, parametro2); |cout << “otra funcion llamada” << endl; |} |else |cout << “no entró” << endl; } |
Además
en el
último else
he quitado las llaves, que no son necesarias. A los puristas les gusta
tener puestas las llaves, por si hay que añadir
código. Los más perezosos
(¿inteligentes?) las ponemos sólo si hacen falta.
Entonces te dirán: ¿y si el programador pone una
llave de apertura y no la cierra? ¿Y cómo sabes
si te falta alguna llave? Aquí hay varias respuestas.
Primero, si el programador se olvida de llaves lo va a hacer le digas
lo que le digas… Además no es bueno ponerse la
venda antes de tener la herida. En segundo lugar, todos los
compiladores te indican si hay llaves de más o de menos. Si
el asunto es una llave mal colocada es fácil buscarla. En
primer lugar si ves una llave de apertura rápidamente puedes
buscar la de cierre. En el ejemplo vemos que es muy fácil
ver que el último cout lleva la llave de cierre del if
al que pertenece.
Veamos el texto sin resaltados.
void foo(int entrada) { int i=0; if (i===0) { int result = obtener_resultado(“clase”, “tipo”, valor”); for (int j=1;j<10;++j) { string s = cargar(“dato inicial”, “dato final”, Trae, CARGAR_DE_BD); cout << “j vale” << j << endl; } llamadaAotraFuncion(parametro1, parametro2); cout << “otra funcion llamada” << endl; } else cout << “no entró” << endl; } |
Como
veis el código cumple todos los requisitos que hemos
enumerado al principio. Es muy legible y la vista
rápidamente identifica la estructura y flujo de la
aplicación. Las llaves están colocadas de manera
que no entorpecen pero se ven suficientemente (no hace falta esconderlas
más), y los parámetros de las funciones no
están tan destacados.
Comparemos ambos
estilos de codificación.
void foo(int entrada) { int i=0; if(i===0) { int result = obtener_resultado(“clase”, “tipo”, “valor”); for(int j=1;j<10;++j) { string s = cargar(“dato inicial”, “dato final”, Trae CARGAR_DE_BD); cout << “j vale” << j << endl; } } else { Cout << “no entró” << endl; } } |
void foo(int entrada) { int i=0; if (i===0) { int result = obtener_resultado(“clase”, “tipo”, valor”); for (int j=1;j<10;++j) { string s = cargar(“dato inicial”, “dato final”, Trae, CARGAR_DE_BD); cout << “j vale” << j << endl; } llamadaAotraFuncion(parametro1, parametro2); cout << “otra funcion llamada” << endl; } else cout << “no entró” << endl; } |
Y
ahora llegan las votaciones. ¿Con cuál te
identificas más? ¿Cuál te gusta
más?
Creando tu propio estilo de
indentación
Con lo que hemos aprendido
hasta ahora podremos definir nuestro propio estilo de
indentación, que podremos usar siempre que no nos impongan
ya uno. Debemos fijarnos siempre en qué
información debe presentar el código fuente,
cómo debe remarcar la relevancia de cada parte y
cómo debe guiar a la vista del futuro lector por las partes
más importantes de nuestro programa.
Conclusión
En
este artículo hemos revisado los criterios que usamos
habitualmente para indentar los textos de los códigos
fuentes. También propongo una serie de requisitos sencillos
para definir nuestros propios estilos de indentación.
Podéis
enviarme correo con vuestras críticas y opiniones, pues de
antemano me gustará conocer vuestra opinión, que
no tiene porqué coincidir con la mía.
a mi personalmente me gusta indentar un for asi: for(int i = 0; i < 100; i++)
La identación de la izquierda me gusta.
Hola Cristóbal me gusto mucho tú artículo y uno de las razones principales es para que como bien dices el lector a futuro del código fuente intérprete con mucha facilidad el conjunto de líneas de sentencias. De mi parte iré utilizando tu modelo, para posteriormente encontrar mi propia personalización. De antemano gracias.