Los fregados en los que me meto
Por Jacobo Tarrío
23 de enero de 2007

Esto no es una pregunta, sino algo que les enseño para qué vean en qué jardines me meto yo solo. Aquí hablo de programación y enseño código en Java y es largo (aunque no tanto como parece), así que si esto no es lo suyo, les eximo de la obligación de leerlo. De todos modos, me he esforzado en hacerlo apto para todos los públicos, así que hagan un esfuerzo, porfa. Luego me dicen en un comentario qué les ha parecido.

Estoy haciendo una aplicación web en Java; por un lado, reaprendiendo cosas que se supone que ya sabía (algo de Java por aquí, algo de Struts por allá, …) y aprendiendo cosas nuevas para mi (Hibernate, Eclipse, etc.).

Esta aplicación es el típico programa de gestión en el que se tiene una base de datos con unas cuantas tablas sobre las que hay que realizar siempre los mismos tipos de operaciones. Es decir, tengo una tabla de clientes y tengo que poder añadir, listar, modificar, buscar y borrar clientes; tengo otra tabla de proveedores y tengo que poder añadir, listar, modificar, buscar y borrar proveedores; tengo otra tabla de productos y tengo que…

Mientras exploraba el entorno y las APIs y, en general, aprendía a programar el programa, me hice un prototipo de la parte que gestiona los clientes: la clase que accede a los datos, las clases que realizan las acciones que solicita el usuario en su navegador, las páginas JSP que muestran los datos y los formularios, etc.

Una vez que funcionó todo, y aprovechando que ya tenía prácticamente definido el esquema de la base de datos, me puse a modelarla en el Hibernate y después hice copias de la clase que accede a los datos del Cliente, modificándolas para las otras clases, y así acabé con unas clases con unos interfaces similares a esto:

public class ClienteOps {
  public Cliente getCliente();
  public void crearCliente();
  public void cargarCliente(long id);
  public void grabarCliente();
  public void borrarCliente();
  public static List listaClientes();
}

public class ProveedorOps {
  public Proveedor getProveedor();
  public void crearProveedor();
  public void cargarProveedor(long id);
  public void grabarProveedor();
  public void borrarProveedor();
  public static List listaProveedores();
}

public class ProductoOps {
  public Producto getProducto();
  public void crearProducto();
  public void cargarProducto(long id);
  public void grabarProducto();
  public void borrarProducto();
  public static List listaProductos();
}

Enseguida vi (en realidad lo sabía antes de empezar) que estaba cometiendo el error fatal de la muerte número 1: me estaba repitiendo más que el ajo. Tenía un montón de código casi igual, y lo estaba copiando, y si había un error en una parte tendría que corregirlo en todas las clases, y si hacía una mejora tendría que arreglarla en todas las clases, y…

Se imponía simplificar esto. En primer lugar, dejar “cargaProducto”, “cargaCliente”, etc., en sólo “carga”, “grabaLoQueSea” en sólo “graba”, etc. En segundo lugar, crear una superclase que contenga las funciones comunes, y dejar lo específico en las subclases:

abstract public class AbstractOps<T> {
  public T get();
  public void crear();
  public void cargar(long id);
  public void grabar();
  public void borrar();
  public static List lista();
}

public class ClienteOps extends AbstractOps<Cliente> { }

public class ProveedorOps extends AbstractOps<Proveedor> { }

public class ProductoOps extends AbstractOps<Producto> { }

Ese <T> es lo que la especificación de Java llama una “variable de tipo”. Lo que quiere decir es que yo puedo poner esa T donde normalmente pondría el nombre de una clase, y después le puedo decir a Java “oye, lo que te dije antes de que era T, pues no, que es Cliente”, o Proveedor, o Producto… Y eso es lo que hice: creé una superclase con toda la funcionalidad, pero donde antes ponía Cliente, o Producto, o lo que fuera, ahora ponía T. Luego creé varias subclases vacías, y en cada una le decía “la clase ClienteOps es una subclase de AbstractOps que hereda toda su funcionalidad sustituyendo T por Cliente”, y así sucesivamente, y así solucionaba el problema sin escribir más líneas de código de las justas. O sea: esto es la pera, Java es la hostia y yo soy un puto genio.

Pues no.

Aquí creo que hay gente que sabe más de Java de lo que yo sabía ayer a estas horas y ya sabrá cuál era el problema; y si no, lo explico. Esto de las variables de tipo sirve para sustituir los nombres de las clases a la hora de compilar en las declaraciones de las variables y de los atributos y de las funciones miembro y de otras clases, etc. Sin embargo, no sirve a la hora de ejecutar para crear una instancia de una clase o para obtener un objeto de una clase, o…

O, viéndolo en código:

class C<T> {
  void fun() {
    T a = new T(T.class);
  }
}

En la línea del medio, la que contiene “new”, la primera T está bien puesta porque está en la definición de una variable. Sin embargo, ni la segunda ni la tercera valen.

Y créanme que en la clase AbstractOps había muchas cosas como la segunda y la tercera T. O sea, que la solución, tal como estaba, no servía. Había que arreglarlo de alguna manera. ¿Cómo? Pues esto es lo que probé:

abstract public class AbstractOps<T> {
  protected static Class<?> clase;
  public T get();
  public void crear();
  public void cargar(long id);
  public void grabar();
  public void borrar();
  public static List lista();
}

public class ClienteOps extends AbstractOps<Cliente> {
  protected static Class<?> clase = Cliente.class;
}

… y así sucesivamente.

El método “lista” es estático, y dentro de él tengo que hacer referencia a la propia clase (para decirle a Hibernate de qué objetos hay que hacer una lista), y no podía obtener la referencia mediante una variable de tipo, sino que tenía que guardarla en una variable estática que definía en cada subclase. Pero, claro, cuando ejecutaba ClienteOps.lista() me salía una excepción como una casa porque intentaba acceder a una variable no inicializada (porque se ejecutaba el método lista() de AbstractOps, que accedía al atributo estático “clase” de AbstractOps, que no está inicializado).

Así que al final me ha quedado algo parecido a esto:

abstract public class AbstractOps<T> {
  protected Class<?> clase = laClase();
  protected abstract Class<?> laClase();
  public T get();
  public void crear();
  public void cargar(long id);
  public void grabar();
  public void borrar();
  public List lista();
}

public class ClienteOps extends AbstractOps<Cliente> {
  protected Class<?> laClase() { return Cliente.class; }
}

Y ahora funciona correctamente. Ahora sí que soy un puto genio. Lo único malo es que así la función miembro “lista()” no es de clase, sino de instancia, pero bueno, no me quejo…

Y, por supuesto, ahora que he contado todo esto, resultará que habrá un método mucho mejor que conoce todo el mundo menos yo, y habré quedado como un idiota :-) Por favor, ilumínenme…

Otros artículos sobre “Tirando Líneas (2005-2008)”, “programación”.
Índice.
Salvo indicación en contrario, esta página y su contenido son Copyright © Jacobo Tarrío Barreiro. Todos los Derechos Reservados. Información sobre tratamiento de datos y condiciones de uso.