Cuando hacemos un método de una clase, tratamos de hacerlo lo más robusto posible. Para ello, normalmente ponemos un montón de if al principio para verificar que todo está como debe antes de empezar a realizar la tarea que debemos. Por ejemplo, suele ser habitual comprobar que los parámetros que nos pasan no son null, quizás que estén dentro de un rango, que no sean negativos, etc, etc.
A veces nos pasa que unos métodos llaman a otros que a su vez llaman a otros y así sucesivamente. Si cada método comprueba sus cosas, lo más fácil es que se hagan las mismas comprobaciones una y otro vez. Por ejemplo, si el metodo1() recibe un parámetro a y comprueba que no es null antes de pasárselo a metodo2(), posiblemente metodo2() también lo comprueba.
Esto hace por un lado que tengamos que codificar de más, una y otra vez las mismas comprobaciones. Por otro lado, el código se vuelve ineficiente, ya que gasta mucho tiempo comprobando lo mismo.
Una posible solución es el llamado “Diseño por contrato“. En este tipo de diseño, para cada método se definen unas precondiciones, unas invariantes y unas postcondiciones. Las primeras son las cosas que tiene que garantizar que ocurren el que llama el método. Las segundas son algo que debe cumplirse siempre (por ejemplo, una edad debe ser positiva), la tercera son las que el método debe garantizar que se cumplen cuando se termina de ejecutar el método.
Por ejemplo, si hacemos un método para el cáculo de raíces cuadradas, puede tener esta pinta
public double raizCuadrada (double operando)
Se pueden poner las siguientes precondiciones y postcondiciones:
- Precondicion: operando debe ser 0 o positivo. No puede ser negativo
- Postcondicion: Se devuelve la raíz cuadrada del número.
Se puede tomar la decisión de poner este “contrato“ en el comentario o en el documento de diseño y NO comprobar dentro del método si operando es 0 o positivo. Se presupone que el que llama debe verificar esa condición y si no se cumple, el método puede hacer cualquier cosa, incluso romper.
Este tipo de diseño tiene dos ventajas:
- Por un lado ahorra escribir código de comprobaciones, evitando como se dijo antes la misma comprobación muchas veces. Hace algo más eficiente el código.
- Por otro lado, deja más claro qué hace exactamente cada método.
Lo mejor de todo es que existen herramientas que permiten definir y comprobar los contratos de cada método.
Proporcionada por el mismo java, tenemos las assertion. Básicamente consiste en poner en el código, al principio del método, algo como
assert operando>0
Si no se cumple, saltará una excepción.
Otras herramientas aparte, pueden ser:
En esta última, por ejemplo, las condiciones se ponen en el comentario del método, con etiquetas nuevas como @pre, @post y @invariant. El código sería algo así
/**
* Comentario
* @pre operando>0
*/
public double raizCuadrada (double operando)
{
…
De todas formas, tengo mis dudas de que esto sea efectivo.
Una cosa es la programación por contrato sin herramientas, que cumple la idea de evitar comprobar muchas veces lo mismo y dejar claro que hace un método. Aunque parece que es dejar el código algo incompleto y poco robusto.
Y otra cosa es meter una herramienta en la que tenemos que meter las condiciones y que luego se verificarán en tiempo de ejecución. Puestos a meterlas, ¿qué mas da hacerlo en código normal que como etiquetas @pre y @post en el comentario?. Las únicas ventajas de estas herramientas parece ser que es “standarizar” cómo poner y comprobar las condiciones que deben cumplirse. Siempre es más cómo leer unos @pre y @post que andar buscando los if dentro del código. También el formato del comentario se hace de esta forma standard y obliga a ponerlo.