Developing Frogtek

El blog del Departamento de Tecnología

Etiqueta: java

Excepciones para controlar tu lógica

Hace unos días apareció en nuestro board una User Story que tenía como título:

“Pop-up warning when Cost is higher than Price”

En sus AT’s aparecían las pantallas donde debería hacerse esta comprobación. Así que nos pusimos manos a la obra y estudiamos cuál sería el impacto de este cambio. Vimos que tendríamos que añadir una condición en todas las pantallas y en todos los sitios donde son susceptibles de cambio las dos variables que nos atañen.

Si optábamos por ese camino, esto iba a oler a bug, en el que te reportan que no se muestra el pop-up en un lugar que no tuviste en cuenta a la hora de añadir la condición. Con lo que decidimos no optar por la solución a priori mecánica y sencilla. De repente, la palabra “Excepciones” vino a nuestras mentes, Rubén de Biko2 estuvo durante tres días inculcando su sabiduria en el equipo de Frogtek y algo de lo que nos contó salía a la luz.

Así que decidimos implementarnos una excepción que se lanzase desde la lógica hacia la UI si esta condición se daba. De este modo el impacto sería mucho menor y desde la UI solo tendríamos que controlar dicha excepción y mostrar el pop-up, y además sería imposible saltarnos ningún sitio, puesto que tendríamos error de compilación. Así quedo nuestra clase:

package org.frogtek.tiendatekcore.exceptions;
  public class CostHigherThanPriceException extends Exception {
        public CostHigherThanPriceException() {
            super("Cost is higher than price");
        }
    }

En este caso el mensaje no es importante para nosotros: solo queremos capturar el momento en el que sucede esta condición.

El método del bean que lanzaba la excepción quedaría así:

    public void setPurchaseCost(long purchaseCost) throws CostHigherThanPriceException {
        if (purchaseCost != this.mPurchaseCost) {
            if (this.mPurchasePrice < purchaseCost) {
                throw new CostHigherThanPriceException();
            } else {
                this.mPurchaseCost = purchaseCost;
            }
        }
    }

Después de esto surgieron por la oficina las dudas filosóficas de si estabamos tomando el camino correcto al usar excepciones para esto, o de si un excepción no debería controlar solo errores que no se pueden manejar. Esta es la definición oficial de java:

An exception is an event that occurs during the execution of a program that disrupts the normal flow of instructions.

Muy abierta y si os dais cuenta no habla en ningún momento de errores. Habla de eventos.

Conclusiones que en Frogtek hemos obtenido:

  • Que tener a una persona de fuera de la oficina con el espíritu y con las ganas de hacer bien las cosas es increíble.
  • Que usar excepciones es una muy buena estrategia para definir tu lógica. Nosotros somos un equipo de 7 personas tocando y retocando el mismo código a diario. Ayuda mucho que tú tengas que llamar a un método que lance una excepción: desde el primer momento estás advertido de que ese método puede lanzar algo y estás en la obligación de controlarlo.

Aquí huele a cuco (IV)

Bueno, un poco de autocrítica nunca viene mal y en este caso yo soy uno de los culpables de este desaguisado….

public int getVendorsCount() {
	if (this.mDm.openDBConnection()) {
		this.mDbHelper = new VendorsDbAdapter();
		Cursor cursor = this.mDbHelper.getVendorsCount();
		if (cursor != null) {
			cursor.close();
			return cursor.getCount();
		}
		this.mDm.closeDBConnection();
	}
	return 0;
}

Fijaos bien: estamos intentando acceder a un cursor después de cerrarlo….. además solo cerramos la base de datos si el cursor es null… vamos, una joya de código. Gracias a que tenemos test funcionales en Android hemos encontrado semejante esperpento. Ya vamos recuperando poco a poco lo invertido en ellos 🙂 .

Así he dejado el código después del pertinente refactor.

public int getVendorsCount() {
	int vendorsCount = 0;
	if (this.mDm.openDBConnection()) {
		this.mDbHelper = new VendorsDbAdapter();
		Cursor cursor = this.mDbHelper.getVendorsCount();
		if (cursor != null) {
			vendorsCount = cursor.getCount();
			cursor.close();
		}
		this.mDm.closeDBConnection();
	}
	return vendorsCount;
}

¿Bastante mejor no? ¿Alguna sugerencia para mejorarlo todavía más?

Primera code kata en frogtek

Hoy hemos tenido la primera code kata. Jose ha preparado una magnifica sesión en la que hemos puesto el pomodoro a 25 minutos para elaborar una solución al problema del Fizz Buzz.

Hemos tenido soluciones muy elegantes, os paso esta por ejemplo de Alberto:

public class FizzBuzz {

	final static String FIZZ = "Fizz";
	final static String BUZZ = "Buzz";
	final static String FIZZBUZZ = "FizzBuzz";
	public static void main(String[] args) {
		for (int i=1; i<=100; i++) {
			System.out.println(getFizzBuzz(i));
			
		}
	}
	
	public static String getFizzBuzz (int number) {
		if (number % 15 ==0){
			return FIZZBUZZ;
		} else if (number % 3 ==0){
			return FIZZ;
		} else if (number % 5 ==0){
			return BUZZ;
		} else {
			return Integer.toString(number);
		}
	}

}

¿Algún comentario? ¿Mejora?

Hemos decidido hacerla en Java, la próxima irá en python y alguien por ahí ha dicho que en LISP.

Los 10 mandamientos de Java

Quizás todavía hemos hablado poco de Sonar, pero es necesario indicar que se trata una de las herramientas indispensables a la hora de crear código elegante. Básicamente, esta utilidad nos permite analizar nuestros proyectos en busca de código poco eficiente o mejorable, ordenando estas infracciones (suena mejor que violaciones) por su gravedad (blocker, critical, major, minor e info).

Con esfuerzo, hemos conseguido eliminar todas las infracciones críticas de nuestros proyectos, pero como sabemos que volveremos a tropezar dos veces con la misma piedra, necesitábamos algún tipo de recordatorio que nos permitiera tener siempre en mente el no volver a cometer esas infracciones.

De ahí surgió la idea de crear una tabla con los diez mandamientos de Java. Aquí tenéis la versión final. Explicaré cada punto en detalle.

Los 10 mandamientos de Java
I. Thou shall not access non-static variables in a static manner.

Esta infracción se repetía bastante en nuestro código. Si tenemos un método estático , no debería poder acceder a variables que no están pensadas para llamarse de forma estática. El compilador lo permitirá, claro, pero manda al garete la filosofía de los métodos estáticos.

II. Do not write a static variable from an instance method. Ever.

Parecida a la anterior, pero al contrario. Si tenemos una variable estática, no deberíamos poder escribirla desde un método perteneciente a un objeto instanciado. Sirva de ejemplo:

private static String mTitle;
...
public void showDialog(String title, String message) {
   mTitle = title;

III. Thou shall not leave empty if-else/switch statements.

¿Cuántas veces habrá pasado? Dejamos un todo en el if y asunto resuelto. Pues a partir de ahora, pecado mortal.

if (item != null) {
   newPurchase();
} else {
   // no purchase yet
}

Aunque no lo pone, esto también se aplica a los bucles vacíos.

IV. Thou shall not repeat code in a if-else/switch statement.

A veces por dejadez, mantenemos dos bloques de un condicional exactamente iguales. Veamos un ejemplo con un switch:

switch(gafas){
   case 1: return "de lejos"; break;
   case 2: return "progresivas"; break;
   case 3: return "del cerca"; break;
   default: return "de lejos"; break;
}

Podría modificarse quitando el caso 1 o fusionándolo con el default.

V. Thou shall not leave a private method not being called from anywhere.

Sencillo: no creemos métodos que no sean utilizados en ninguna parte. Si el título del metodo tiene un rayica amarilla, mal asunto. Siguiente.

VI. Remember to not null-check a value which is already null.

Si estamos seguros al 100% de que algo tiene como valor null, no tiene sentido hacer una comprobación. Lo mismo para el caso contrario: si es imposible que una variable tenga valor null, resulta redundante el comprobarlo.

VII. Thou shall not use short variable names.

Pecadores todos aquellos que utilizáis nombres de variables como “cursor c”, “RelativeLayout rl” o “String s”. Quedan excluidas las variables de iteración dentro de un bucle.

VIII. Do not take the visibility of variables in vain

Qué bonito sería el mundo si todas las variables fueran public. Nos ahorraríamos más de un disgusto. Pero si una variable sólo se utiliza dentro de el entorno de una clase, debemos declararla como private, o arder en el infierno. Lo mismo con protected, claro.

IX. Remember to write javadocs and keep it holy.

Método que dejamos sin Javadoc, 10 flagelaciones.

X. Avoid duplicate literals, if it is repeating, create a variable.

if (numPiscinas == 1) {
   show("mensaje", 1);
} else if (numPiscinas == 2) {
   show("mensaje", 2);
} else if (numPiscinas >= 3) {
   show("mensaje", 4);
}

Si la cadena “mensaje” es usada en el código continuamente, mejor escribir una variable propia con la cadena, que seguro que nos resuelve la vida.

Y eso es todo. Podemos crear código sin hacer caso de estos mandamientos, y obtendremos un buen resultado. Pero seguro que si los seguimos a rajatabla, adquirimos unas costumbres de programación muy saludables. Por supuesto, estos puntos son cuestionables, no se trata de los más críticos, sino simplemente de los que más suelen repetirse en nuestro código. Os animo a que elaboréis vuestra propia lista de mandamientos y la colguéis bien alta para tenerla siempre en mente. Y para acabar, me despediré con una frase de Charles Reade:

“Siembra un acto y cosecharás una costumbre. Siembra una costumbre y cosecharás un carácter. Siembra un carácter y cosecharás un destino.”