Cómo es el tratamiento de errores en programación orientada a objetos

COMPARTIR 0 TWITTEAR

gestion_errores1

Siguiendo nuestro curso de programación nuestro siguiente paso es tratar los errores que pueda tener nuestro programa. Esto no es algo específico de la programación orientada a objetos, si no de todas las metodologías, ya que es muy importante este tratamiento y siempre, obviando la metodología que utilicemos tenemos que tener en cuenta un tratamiento para los posibles errores.

A la hora de tratar los errores los errores que nuestro programa genere nos surgen generalmente tres grandes escenarios:

  • Que el usuario comenta un error a la hora de comunicarse con el programa (por ejemplo en la entrada de datos).
  • Que el programador no diseñe bien el programa y este por tanto este mal programado.
  • O bien que el sistema sobre el que se ejecuta el programa sea inestable o no este funcionando correctamente.

A la hora de programar principalmente tenemos que mirar nuestros fallos. Parece lógico que debamos tener programa que siempre funcione, que siempre haga la función para la que ha sido programado, pero también tenemos que ser nosotros los que nos preocupemos porque el usuario no cometa errores a la hora de controlar nuestro programa. También aunque en menor medida, tenemos que preocuparnos que nuestro código este lo suficientemente optimizado para que se pueda ejecutar sin que el sistema se vuelva inestable.

Si atendemos a todo esto lo que tendremos que hacer a la hora de programar es tener en cuenta las cosas que podrían salir mal

Prever las cosas que pueden salir mal

Cuando hablamos de prever posible errores estamos hablando de la programación defensiva. La programación defensiva no es una metodología en si, es una técnica implementada en muchas otras metodologías por lo que nuestra mayor prioridad es que nuestro programa funcione. Una extensión de esta programación defensiva es la programación por contratos, pero esto ya lo veremos.

Tenemos que tener claro que cuando hablamos de prevenir las cosas lo que vamos a prevenir son los defectos en el software. Los defectos del software no son simples “molestias”:

  • Un producto con defectos deja de usarse
  • Puede producir enormes pérdidas (incluso de vidas humanas, si muchos accidentes en la NASA son por este motivo un software desarrollado con defectos)
  • Producen retrasos en las entregas, enormes dificultades técnicas… y son responsables de muchas horas extraordinarias.

¿Cómo podemos evitar producir programas defectuosos? en realidad no hay forma, la programación en gran medida trabaja con el prueba y error, de alguna forma tienes que comprobar que algo funciona, pero podemos empezar con lo más simple:

  • Implementación incorrecta: no cumple con la especificación. Por ejemplo: solicitar a un objeto que haga algo que es incapaz de hacer, es decir, llamar a un método que el objeto no lo tiene implementado.
  • Nunca debemos invocar al método get de una colección con un índice no válido o invocar a un método con un objeto null, o acceder a una posición inexistente de una lista.
  • Acciones erróneas del usuario como. Por ejemplo: introducir una letra al teclear un número.
  • Condiciones de ejecución que imposibilitan el cómputo. Ejemplos: no queda memoria o espacio en disco, falla el hardware.
  • También otros ajenos como: un fallo en la red eléctrica, un fallo en la lectura de un fichero …

En fin un montón de posibles errores, que ahora conocemos, pero eso si una pequeña parte, pues los errores que podemos cometer son muchísimos. Tenemos que tener claro que nuestro objetivo es evitar producir programas defectuosos mediante 4 pasos:

  • Fundamentos del desarrollo (análisis, diseño, buena codificación)
  • Depuración
  • Buen tratamiento de errores
  • Pruebas y documentación

Java es nuestro amigo (de verdad, lo es)

Más que Java, el compilador de Java detecta muchos errores estáticamente (en tiempo de compilación). Por ejemplo, los errores de sintaxis y los errores de tipo. Pero, hay errores que se comprueban en tiempo de ejecución, debido a su carácter dinámico como por ejemplo:

  • Pasar un mensaje a una referencia que apunta a null.
  • División por cero.
  • Acceso a un Array con un índice fuera de los límites.
  • Valores de argumentos inválidos (Por ejemplo: el factorial de un número negativo).

Es necesario disponer de un mecanismo para manejar los errores de ejecución.

Tenemos que tener un mecanismo

Un mecanismo para controlar los errores de ejecución debe ser

  • Robusto: El mecanismo debe obligar al programador a escribir código para manejar los errores de ejecución. Por ejemplo: ¿qué sucede si un archivo no se puede abrir?
  • Reusable: Podemos reutilizar el código, implementando diferentes manejadores de error en diferentes escenarios o siguiendo nuestra terminología, estos mecanismos los podremos utilizar en todas nuestras clases.
  • Extensible: Permita añadir nuevos tipos de errores y extender los ya existentes (nosotros esto ya sabemos como hacerlo: con la herencia).

Entonces, vamos a resumir todo esto en que nuestro programa tendrá que tener una parte de código específico en previsión de fallos:

  • Verificando las posibles situaciones de error.
  • Adoptando medidas si el error se produce.
  • Generando información que permita localizar el error.

Mecanismos

Ya lo tenemos claro, si queremos que nuestro programa siempre se comporte de manera correcta debemos tener en nuestro código una parte que se encargue de este tratamiento. Bueno entonces vamos a llevarlo a nuestra clase Granja.

Quizás no nos hallamos fijado, pero en nuestra Granja podemos añadir objetos null. Si ejecutamos:

Granja g = new Granja("granja1");
Cerdo cerdo = new Cerdo("Cerdo Manolo");
Vaca vaca = new Vaca("Vaca Marisa");

g.añadirAnimal(cerdo);
g.añadirAnimal(vaca);   
g.añadirAnimal(null);

g.muestraAnimales();

Tendremos una salida de pantalla similar a:

error_añadir_animal_null

Esto que hemos realizado no es nada común hacerlo, es más incluso podríamos decir que está mal. Tenemos que tener claro que los mecanismos para tratamiento de errores debe siempre notificar el error por tanto esto no esta bien porque:

  • No es robusto porque el cliente piensa que no hay errores.
  • No es reusable ni extensible porque el error no se notifica.

Tenemos que tener una cosa clara, en ocasiones es vital parar el programa cuando se detecte un problema, pero en la gran mayoría de los casos no, debemos capturar ese programa, tratarlo y notificarlo. Será cuestión de diseño determinar cuando un problema tiene que abortar la ejecución de un programa.

En nuestro ejemplo vemos como se nos ha notificado del error, pero no hemos lo hecho nosotros, lo ha realizado Java y nos ha impedido continuar con la ejecución del programa.

Comprobación de un problema

Hay dos formas de comprobar un problema, antes de que se cometa o después de que se cometa.

  • Comprobación a priori : Permite comprobar el estado antes de llamar al método. Sí es reusable y extensible, pero no es robusto, porque el cliente no está obligado a comprobar el estado antes de la invocación del método.
  • Comprobación a posteriori: Permite comprobar si ocurrió un error en la invocación del método anterior. Sí es reusable y extensible. No es robusto, porque el cliente no está obligado a comprobar el estado después de la invocación del método.

Si nos fijamos en nuestro ejemplo de la clase Granja habíamos realizado una comprobación del error después de cometerlo. Entonces, si ni antes ni después vale, ¿Cuando comprobamos el error?

Podemos añadir un código de comprobación por ejemplo:

public void añadirAnimal(Animal animal) {
    if (animal == null)
        System.err.println("Error al intentar añadir un animal null");
    else
        animales.add(animal);
}

Cuando lo ejecutemos tendremos un mensaje como:

Error al intentar añadir un animal null

Lo hemos solucionado, pero igual tenemos Java ya incorpora mecanismos para tratarlo.

Tratamiento del error

Debido a la falta de mecanismos para la gestión de los errores de ejecución, Java incorpora el manejo de excepciones en tiempo de ejecución. El código de manejo de excepciones se separa en:

  • Detectar el error y se “lanza” una excepción.
  • El manejo de excepciones fuerza el tratamiento de los errores.

Si nos fijamos nosotros en nuestro método añadir siempre estábamos comprobando que no añadiésemos un objeto null a nuestra lista de animales. Podemos mejorar lo anterior añadiendo el manejo de excepciones al método que en realidad accede al objeto null:

public void muestraAnimales() {
    for (int i = 0; i < animales.size(); i++)
        try{
        System.out.println(animales.get(i).getNombre() + " con peso "
                + animales.get(i).getEdad());
        }catch (Exception e) {
            System.err.println("Error al añadir un elemento null");
        }   
}

Y dejamos nuestro método añadir animal de la misma forma que teníamos:

public void imprimirNombre() {
    System.out.println("El nombre de la granja es: " + nombre);
}

Si nos fijamos ahora ya no tenemos el mensaje del error cuando añadimos los animales, si no cuando accedemos a la lista de los animales para imprimirla por pantalla.

Hasta aquí esta introducción al tratamiento de errores, en los siguientes artículos indagaremos más en este tipo de tratamiento de errores, con excepciones. Mientras tanto podéis descargar el código y probarlo.

Archivado en Curso de Programación, Java, Tratamiento de errores
COMPARTIR 0 TWITTEAR

Comentarios (6)

Usa tu cuenta de Facebook para dejar tu opinión.

Publica tu opinión usando tu cuenta de Facebook.

¿Te ha gustado? ¡No te pierdas nada más!

follow us in feedly

Otras webs de Difoosion