martes, 5 de mayo de 2009

Te declaro por un rato...

Hace tiempo me tocó subirme al carro de la programación en Java y, en especial, programación J2EE con toda la parafernalia asociada (mdb´s, ejb´s, etc.). Yo tengo una base en programación de software sobre lenguajes estructurados (C, Pascal) y Orientados a Objetos (C++) principalmente. Adicionalmente, por inquietud profesional, es decir, por los pitutos que he realizado, tengo experiencia en lenguajes del mundo Microsoft previos a .net (Visual Basic 6.0, ASP, etc.) y, ahora último, C#. Estos últimos conocidos como "El Lado Oscuro" por mis pares amantes de las infraestructuras del tipo LAMP. En este camino de descubrimiento de las ventajas y bondades de la programación en Java, uno de los aspectos que más me molestó es la insoportable sintaxis y/o estándar de facto relacionado a la programación J2EE para el uso de recursos y/o objetos determinados.

Un claro ejemplo de esto es el uso de mecanismos de Log en estos ambientes. Una sintaxis típica de una instrucción de Log en este contexto se ve como sigue:

  :
FactoryManager.getLogFactory().getLogger().Log("Ha ocurrido un error");
  :

A simple vista, no tiene mucha complejidad este código, sin embargo, si vemos un código que haga uso extensivo de esta sintaxis se vería así:

FactoryManager.getLogFactory().getLogger().Log("Comenzado proceso de Analisis");
FactoryManager.getLogFactory().getLogger().Log("Analizando Bloque 1");
  :
FactoryManager.getLogFactory().getLogger().Log("Analizando Bloque 2");
  :
FactoryManager.getLogFactory().getLogger().Log("Proceso de Analisis Terminado");
FactoryManager.getLogFactory().getLogger().Log("Bloques Correctos " + ....);
FactoryManager.getLogFactory().getLogger().Log("Bloques con Error " + ....);

Desde mi punto de vista, este código tiene los siguientes problemas:

  • Genera más código del necesario (en lo visual y en bytes).
  • Es innecesariamente complejo dado que no facilita procesos de mantención asociados, en este caso, a la clase Log, LogFactory y FactoryManager. Básicamente, porque un cambio menor en cualquiera de ellas, podría rápidamente impactar las 7 líneas anteriores. Peor aún cuando el código sobre el que se está operando tiene miles de líneas.
  • No hace ni fomenta un correcto control de errores para la obtención del LogFactory ni para la recuperación del objeto Logger (aunque debieran ser implementaciones con un Singleton y/o variables static, cualquiera de estas dos podría retornar un error y la sintaxis no lo refleja ni aborda).

Para evitar estos problemas, mi recomendación es el uso de variables de referencia, es decir, variables temporales destinadas única y exclusivamente a resolver los problemas anteriores. Estas variables no tienen ningún impacto en la lógica implementada.

El código anterior podría reescribirse como sigue:

LogFactory oLF = FactoryManager.getLogFactory();
Logger oLog = oLF.getLogger();

oLog.Log("Comenzado proceso de Analisis");
oLog.Log("Analizando Bloque 1");
  :
oLog.Log("Analizando Bloque 2");
  :
oLog.Log("Proceso de Analisis Terminado");
oLog.Log("Bloques Correctos " + ....);
oLog.Log("Bloques con Error " + ....);

Si incorporamos la validación de condiciones de error para los objetos indicados antes, el código quedaría como sigue:

LogFactory oLF = FactoryManager.getLogFactory();

if( oLF == null ) {
  // Determinar qué hacer en esta condicion de error
}

Logger oLog = oLF.getLogger();
if( oLog == null ) {
  // Determinar qué hacer en esta condicion de error
}

oLog.Log("Comenzado proceso de Analisis");
oLog.Log("Analizando Bloque 1");
  :
oLog.Log("Analizando Bloque 2");
  :
oLog.Log("Proceso de Analisis Terminado");
oLog.Log("Bloques Correctos " + ....);
oLog.Log("Bloques con Error " + ....);

Desde mi perspectiva este código es más claro, fácil de mantener y visualmente simple. En el caso de clases que se utilicen reiteradamente y que el uso del log sea una obligación, si el acceso al "Logger" no presenta grandes problemas, una clase podría definir internamente una referencia al Log para ser utilizada por todos los métodos de la misma como sigue:

class A {
  LogFactory oLF = null;
  Logger oLog = null;

  public A() { // Constructor
    oLF = FactoryManager.getLogFactory();

    if( oLF == null ) {
      // Determinar qué hacer en esta condicion de error
    }

    oLog = oLF.getLogger();
    if( oLog == null ) {
      // Determinar qué hacer en esta condicion de error
    }
  }

  void metodoUno(...) {
    :
    oLog.Log("Ejecutando Metodo Uno");
    :
  }

  void metodoDos(...) {
    :
    oLog.Log("Ejecutando Metodo Dos");
    :
  }
}

Lo anterior no aplica para clases con métodos estáticos y/o escenarios en donde el destino del "Log" es dinámico (archivos, base de datos, e-mail, etc.).

Otro ejemplo que me ha tocado ver en donde lo anterior aplica y produce mejoras sustanciales es en el contexto de la programación de Servlets. En lo personal, he encontrado pocas justificaciones para hacer uso de esta tecnología (en vez de JSP), sin embargo, reiteradamente aparece sintaxis como la siguiente:

public class MiServlet extends HttpServlet {
  private static final long serialVersionUID = 1L;

  public void init(ServletConfig servletConfig) throws ServletException {
    super.init(servletConfig);
  }

  protected void doGet(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException {
    httpServletResponse.getWriter().write("<tr ....");
    httpServletResponse.getWriter().write(" <td ....");
    httpServletResponse.getWriter().write(" </td ....");
    httpServletResponse.getWriter().write(" <td ....");
    httpServletResponse.getWriter().write(" </td ....");
    httpServletResponse.getWriter().write("</tr");
  }
}

Y que, aplicando lo descrito anteriormente, se puede dejar como sigue:

protected void doGet(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException {

  java.io.PrintWriter oPage = httpServletResponse.getWriter();

  oPage.write("<tr ....");
  oPage.write(" <td ....");
  oPage.write(" </td ....");
  oPage.write(" <td ....");
  oPage.write(" </td ....");
  oPage.write("</tr");
}

Generando un código bastante más simple y manejable desde mi punto de vista.

2 comentarios:

zongo dijo...

Tienes toda la razón querido.

zongo dijo...

El otro día boludeando en el lado oscuro tuve que crear una clase que tenía en su constructor un map.
perro con dos campos id y nombre.
Entonces para llenar el map hice lo sgte:
Hagámoslo con un perro:

Perro p1 = new Perro();
map["p1"] = p1;
p1.id = "p1";
p1.nombre = "FoxTerrier";

Luego al traer el map es decir
Console.print(map["p1"].nombre) el resultado es "";

Pero si se hace lo sgte:
Perro p1 = new Perro();
p1.id = "p1";
p1.nombre = "FoxTerrier";
map["p1"] = p1;
y hacemos map["p1"].nombre el resultado será "FoxTerrier".

Cualquier comentario al respecto estoy atento al blog.