sábado, 21 de agosto de 2010

Uso avanzado de Enumeration

No parece que el tema de las cachés interese demasiado, así que mientras decido si continúo o no con el tema, otro consejo breve, en este caso sobre el uso de las enumeraciones en Java.
El uso básico que todo el mundo aprende rápidamente es el de simplemente hacer una lista de elementos para luego, por ejemplo, poderlos pasar como parámetros con verificación de tipos. Algunos desarrolladores, si embargo, se quedan ahí y piensan que su utilidad es limitada puesto que se puede sacar el valor con name(), se puede obtener el índice con ordinal() pero si necesitamos más, tenemos que complicar mucho la cosa... y total para pasar una cadena...
Nada más lejos de la realidad, puesto que extender una enumeración para que nos sea más útil es tremendamente sencillo.
Pongamos un caso parecido a uno con el que trabajé hace poco: Queremos mostrar una lista de empleados de forma ordenada, pudiendo pasar el criterio de ordenación como parámetro, o sea una cadena. Para ordenar una lista de elementos necesitamos un Comparator y no queremos ni que el parámetro a pasar sea un nombre muy largo ni que el nombre de la enumeración sea demasiado corto ni se aparte de las convenciones de código que usamos, así que... ¿como lo podemos hacer?
Muy sencillo:
  • Añadimos dos campos a la enumeración: acrónimo y comparador y los inicializamos al crear cada elemento.
  • Dado que querremos obtener el elemento correcto a partir del acrónimo, no del nombre completo, creamos una tabla de búsquedas inversa, indexada por acrónimos. La creamos estática e inicializada al cargar la clase y siempre la tendremos disponible y preparada, para no tener que buscar en un bucle.
Y el código es igualmente sencillo:
  static Comparator ComparadorPorNombre = new Comparator()
  {
   //...
  };
  static Comparator ComparadorPorSueldo = new Comparator()
  {
   //...
  };
  static Comparator ComparadorPorCargo = new Comparator()
  {
   //...
  };

  public enum Ordenacion
  {
    ORDENAR_POR_NOMBRE(ComparadorPorNombre, "nombre")
    ,ORDENAR_POR_SUELDO(ComparadorPorSueldo, "sueldo")
    ,ORDENAR_POR_CARGO(ComparadorPorCargo, "cargo");
    
    private static Map<String, Ordenacion> mapaAcronimoOrdenacion;

    private final String acronimo;
    private final Comparator comparador;

    private Ordenacion(Comparator comparador, String acronimo)
    {
      this.comparador = comparador;
      this.acronimo = acronimo;
    }

    public Comparator getComparador()
    {
      return comparador;
    }

    public String getAcronimo()
    {
      return acronimo;
    }

    // Método para obtener una ordenación según el acrónimo que nos hayan pasado.
    // Devuelve null si la ordenación no existe.
    public static Ordenacion getPorAcronimo(String acronimo)
    {
      return mapaAcronimoOrdenacion.get(acronimo);
    }

    // Inicializamos el mapa inverso por acronimo al cargar la clase, también
    // podríamos hacerlo de forma "lazy" en el primer acceso, si quisiéramos.
    static
    {
      mapaAcronimoOrdenacion = new HashMap();
      for (Ordenacion rt : Ordenacion.values())
      {
        mapaAcronimoOrdenacion.put(rt.getAcronimo(), rt);
      }
    }
  }

  // Un breve ejemplo de como sería el uso.
  public static void main(String[] arg) throws Exception
  {
    //...
    String param_acronimo = ""; // lo obtenemos de alguna forma
    Ordenacion ord = Ordenacion.getPorAcronimo(param_acronimo);
    Set listaOrdenada = new TreeSet(ord.getComparador());
    //...
  }

De esta forma, podemos añadir nuevas ordenaciones "tranquilamente" añadiendo elementos a la enumeración y podemos saber que en todos los métodos a los que le pasemos una Ordenacion como parámetro seguirán funcionando, y que el control de si existe o no una Ordenacion con ese acrónimo se produce en un punto único, lo cual facilita el mantenimiento.

Lo que he mostrado con un acrónimo y un comparador se puede aplicar a cualquier cosa que se os ocurra: Enumeración con tipos de datos admitidos y expresiones regulares para comprobar si el parámetro es correcto traducciones de acrónimos/códigos numéricos a nombres completos para mostrar al usuario en listas cortas que no estén en BDD...

La cuestión es que si tenéis una lista de elementos y estáis pasando int o String y luego haciendo if/else o switch en varias partes de vuestro código, re-pensadlo a ver si podéis usar una enumeración y hacer el código "type safe" con la traducción de input -> valor correcto en un sólo punto.

Espero que os sirva y...
Happy coding! EJ

No hay comentarios:

Publicar un comentario