Material de estudio  >  Apuntes  >  Escritura de programas  >  Estilo de Programación

Estilo de Programación

José A. Mañas <jmanas@dit.upm.es>
Dept. de Ingeniería de Sistemas Telemáticos
Universidad Politécnica de Madrid
8 de mayo, 2003

1. Introducción

El código de un programa lo leen muchas personas, bien para repararlo, bien para ampliarlo o, simplemente, para evaluarlo. Para estos lectores es fundamental que el programa esté bien redactado, con estilo, para que su significado sea claro y diáfano.

Aún reconociendo que redactar con buen estilo es más un arte que una ciencia, sí existen una serie de reglas básicas que ayudan a conseguir un texto satisfactorio. En esta nota se incluyen este tipo de reglas.

Al igual que cuando aprendemos redacción literaria, una forma excelente de adquirir un buen estili es leer textos ajenos. Es por ello que recomendamos al alumno la lectura, y aún estudio, de programas existentes. Un programa puede escribirse con mejor o peor estilo. En estas notas intentaremos concretar qué se entiende por un programa bien escrito.

Que un programa funcione o no es en buena medida independiente de que esté bien o mal escrito. No obstante:

  1. un programa mal escrito tiene una probabilidad mayor de no funcionar correctamente
    un buen estilo facilita la corrección
  2. es imposible depurar un programa mal escrito
  3. es imposible mantener un programa mal escrito
Para determinar si un programa está bien escrito estimaremos su legibilidad o capacidad de comunicar lo que hace a otros programadores que tengan que leer el código fuente (para evaluarlo, mantenerlo o repararlo). Por ello escribir un buen programa es tan difícil como escribir un buen libro.

Un programador dispone de cuatro mecanismos básicos para comunicarse con sus lectores:

  1. el estilo
  2. los comentarios
  3. los nombres de las variables, constantes y métodos
  4. los espacios en blanco (el sangrado)
A continuación se recogen una serie de reglas de buen estilo.

2. Reglas de buen estilo

2.1. Los ficheros de programa deben organizarse de la siguiente forma:
1. documentación (javadoc) de la clase o interfaz
2. class o interface
3. variables de clase (static)
4. variables de instancia u objeto
5. constructores
6. demás métodos
Un orden sistemático facilita la búsqueda al lector.

2.1. Sobre las variables

2.1.1. Las variables de clase u objeto jamás deberían ser públicas.
Para que nadie pueda alterarlas desde fuera de la clase.

2.1.2. Las variables deben ver reducida su existencia al mínimo tiempo posible;
es decir, declararse en el ámbito más reducido que sea posible.
Para que nadie se confunda en su uso.

Se preferirán variables de método a variables de objeto.

2.1.3. Las variables deben inicializarse inmediatamente.
  int nMuescas= 0;
En previsión de que la variable contenga un valor fuera de control.

2.1.4. Las variables con un ámbito muy amplio deberán tener nombres largos y muy significativos.
Las variables con un ámbito muy restringido pueden tener nombres muy cortos.
  int i, j;
  char c;
  String s;
Las variables que van a retener valores importantes en otras zonas del programa, deben ser retenidas en la memoria del lector, para lo que necesita un nombre suficientemente largo y descriptivo de su contenido.

En cambio, las variables de usar y tirar, que solo van a usarse en las próximas líneas, pueden tener cualquier nombre, pues no necesitan ser retenidas por el lector. Es más, si el lector ve una variable de nombre muy corto, intuitivamente pensará que es una variable sin futuro, que no necesita retener pues tiene la declaración al lado.

2.1.5. Las variables de iteración deben tener nombres muy cortos: i, j, k, ...
  while (Iterator i= xxx.iterator(); i.hasNext(); ) {
    ... ... ...
  }

  for (int i= 0; i < tabla.length; i++) {
    ... ... ...
  }
Porque es habitual en matemáticas usar nombres de una letra en iteradores.

2.1.6. Las variables jamas deben tener un doble signitifado.
  int valor= 0;	// mal: todo son valores
Porque no ayuda a saber para qué sirve una variable.

2.1.7. Jamás use la misma variable para retener dos cosas diferentes.
  int numero= 0;
  numero= cuentaOvejas();
  numero= cuentaSillas();
Primero, porque no ayuda a entender para qué sirve la variable.
Segundo, porque antes o después el programador la usará con el contenido inadecuado.

2.2. Sobre las expresiones

2.2.1. Evite expresiones (numéricas o condiciones lógicas) complejas.
Si una expresión se complica, considere la creación de variables auxiliares o métodos auxiliares (posiblemente privados).
MAL
  if ((elementNo < 0) ||
      (elementNo > maxElement)||
      elementNo == lastElement) {
    ... ... ...
  } 
  
BIEN
  boolean isFinished= (elementNo < 0) || (elementNo > maxElement);
  boolean isRepeatedEntry= elementNo == lastElement;
  if (isFinished || isRepeatedEntry) {
    ... ... ...
  } 
  
Para que sea legible e inteligible.

2.2.2. En una construcción condicional, ponga el caso normal en la parte if y el caso excepcional en la parte else
  boolean isError = readFile (fileName);
  if (!isError) {
    ... ... ...
  } else {
    ... ... ...
  } 
Para que sea legible e inteligible.

2.2.3. Evite comparar con TRUE o FALSE para tomar decisiones.
MAL
  variable= expresion == true;
  variable= expresion == false;
  if (condicion == true) ...
  if (condicion == false) ...
  while (condicion == true) ...
  while (condicion == false) ...
  for (inicio; condicion == true; siguiente) ...
  for (inicio; condicion == false; siguiente) ...
  return condicion == true;
  return condicion == false;
  
BIEN
  variable= expresion;
  variable= ! expresion;
  if (condicion) ...
  if (! condicion) ...
  while (condicion) ...
  while (! condicion) ...
  for (inicio; condicion; siguiente) ...
  for (inicio; ! condicion; siguiente) ...
  return condicion;
  return ! condicion;
  
Para que sea legible e inteligible.

2.3. Sobre las estructuras de programa

2.3.1. No use las construcciones break o continue,
salvo que sea estrictamente necesario.
Para que los bucles sean legibles, sin sorpresas.

2.3.2. En un método que tiene que devolver un resultado dependiendo de varias condiciones;
no acumule el resultado: devuélvalo en cuanto lo sepa.
Se puede lanzar una excepción o retornar un valor.
  int salario (Empleado empleado) throws Exception {
    if (empleado == null) throw new Exception("empleado null");
    int base= salarioBase[empleado.cargo()];
    if (empleado.despedido()) return 0;
    if (empleado.deBaja()) return base*0.8;
    if (empleado.plusProductividad()) return base*1.1;
    return base;
  }
Buscamos leer lo menos necesario para tomar una decisión.

2.3.3. Un bucle no debe ocupar más de una pantalla (unas 20 líneas de código) desde su inicio hasta su final, incluyendo líneas preliminares para inicializar variables.

Considere la oportunidad de "aplanar" el código introduciendo métodos auxiliares con nombre propio.

Para poder leerlos en pantalla.

2.3.4. Evite el anidamiento excesivo de estructuras
for, while, if, switch,

Si tiene más de tres (3) estructuras anidadas, considere la oportunidad de "aplanar" el código o de introducir métodos auxiliares con nombre propio.

MAL
  String url= null;
  if (propertyList != null) {
    for (int i= 0; i < propertyList.size(); i++) {
      Properties prop= (Properties)propertyList.elementAt(i);
      if (prop != null) {
        if (prop.containsKey("url")) {
          url= prop.getProperty("url");
          break;
        }
      }
    }
  }
  

BIEN
  String url= getProperty(propertyList, "url");
  ... ... ...

  private String getUrl (List propertyList, String key) {
    if (propertyList == null)
      return null;
    for (int i= 0; i < propertyList.size(); i++) {
      String value= getProperty((Properties)propertyList.elementAt(i), key);
      if (value != null)
        return value;
    }
    return null;
  }

  private String getProperty (Properties prop, String key) {
    if (prop == null)
      return null;
    return (String)prop.getProperty(key);
  }
  

Para que el programa no se convierta en un laberinto.

3. Referencias