Javascript y los enlaces

julio 24, 2006

Desde hace algún tiempo he notado que la gente tiene cierta manía a escribir los enlaces HTML utilizando el atributo onClick de la etiqueta Anchor (<A>) en lugar de utilizar el atributo href. No sé de dónde ha salido esa técnica, incorrecta en la mayoría de los casos, que suele producir efectos no deseados. Es lo malo de que muchas aplicaciones Web estén desarrolladas por auténticos incompetentes.

El otro día un amigo me pidió ayuda para revisar un problema que tenía que resolver, y justamente el problema era causado por un mal uso de los enlaces HTML. Lo primero que estaba mal es que el enlace no tenía valor en el href, sino que utilizaba directamente el onClick. Dicho onClick ejecutaba una función Javascript que en lugar de devolver la URL a la que dirigirse, hacía window.location=URL. Me entra urticaria de sólo recordar ese código abominable.

El efecto era que se lanzaban 2 threads al servidor y uno de ellos cortaba la ejecución del otro. Es decir, igual que ocurriría si pongo una URL en el navegador, le doy a enter y mientras está accediendo al servidor Web, pongo otra y le doy a enter. La primera conexión se cancela. Esto puede tener varios efectos no deseados. En general no pasa nada malo, pero a veces, ocurre lo que a mi amigo: el URL malo cancelaba al URL bueno.

La cuestión es que el problema de todo esto surge por una seria ignorancia y absoluta desidia del programador que escribió dicho código en primer lugar. Por ello, y para que no le pase lo mismo a otras personas, aquí pongo un enlace a un blog donde se explica muy bien cómo funciona la etiqueta <A> y sus efectos al usar href y onClick.

A modo de resumen, transcribo la tabla final donde se enumeran los casos posibles.

<a href=”#”>click</a> No hace nada, pero desplaza hacia arriba
<a href=”javascript:;”>click</a> No hace nada y no desplaza
<a href=”javascript:f()”>click</a> Ejecuta la funcion f() y sustituye el contenido de la página actual con resultado de esta función (excepto si f() no devuelve nada)
<a href=”javascript:void(f())”>click</a> Ejecuta la funcion f() pero no hace nada más, aunque f() devuelva algun valor.
<a href=”javascript:void(a())” onclick=”b()”>click</a> Ejecuta primero la funcion b() y después a() pero no hace nada más
<a href=”javascript:void(a())” onclick=”return c()”>click</a> Ejecuta primero la funcion c() y, si esta devuelve true entonces ejecuta la función a(). Después no hace nada más
<a href=”javascript:a()” onclick=”return c()”>click</a> Ejecuta primero la funcion c() y, si esta devuelve true entonces ejecuta la función f() y sustituye el contenido de la página actual con resultado de la función.

Más información aquí.

Ajax: una definición sencilla

mayo 24, 2006

Llevo tiempo escuchando la palabra "Ajax" en infinidad de conversaciones y en múltiples páginas técnicas, pero no es fácil encontrar una buena definición en castellano de qué es, para qué sirve y cuándo usarlo.

Lo primero que hay que tener claro es que el término Ajax se está convirtiendo en una de esas palabras que hay que usar porque sino parece que no estás a la moda. Grave error. La tecnología no se lleva bien con las modas. Si es útil, se usa, sino no. Y recordemos que no hay una herramienta o tecnología mágica que lo resuelve todo.

¿Qué es Ajax?

Ajax es el nombre que recibe el proceso de utilizar un objeto JavaScript para intercambiar información en formato XML con el servidor sin tener que hacer submit de un formulario o poner una URL en el navegador: el famoso XmlHttpRequest. Eso es todo, no tiene más. Básicamente, mediante programación JavaScript se puede crear un objeto de tipo XmlHttpRequest que realice una petición a una URL determinada y encapsule el resultado en un arbol XML. Nota: en el caso de Internet Explorer, el objeto en cuestión se llama XMLHTTP.

¿Cómo funciona?

Para poder utilizar esta técnica, es necesario contar con:

  • Un navegador que soporte XMLHTTP o XmlHttpRequest.
  • Un servidor que sea capaz de responder peticiones HTTP en formato XML.

En general, esta combinación siempre está disponible gracias a que los navegadores más importantes soportan esta tecnología y los servidores HTTP suelen permitir cualquier tipo de lenguaje de la familia SML.

¿Cómo lanzar una petición Ajax?

Para ejecutar una petición Ajax, hace falta crear el objeto JavaScript correspondiente:

if (window.XMLHttpRequest) {
   req = new XMLHttpRequest();
}
else if (window.ActiveXObject) {
   req = new
ActiveXObject("Microsoft.XMLHTTP");
}

Este extracto de código creará el objeto que utilizaremos para lanzar las peticiones. Vale para todos los navegadores. Ahora habría que decirle qué URL ejecutar y abrir la conexión:

req.open("GET", url, true);

El primer parámetro es el tipo de petición: GET ó POST, el segundo parámetro es la URL y el tercer parámetro indica si debe ser una conexión asíncrona (true) o síncrona (false). En Ajax, las conexiones son asíncronas (la "A" de Ajax es "Asynchronous"). Cuidado al poner false, porque el navegador se quedará bloqueado hasta que esta conexión finalice.

El próximo paso es asignar un valor a la propiedad onreadystatechange. Esta propiedad registra una función Javascript que se ejecutará cuando la conexión finalice.

req.onreadystatechange = processXMLResponse;

También se pueden establecer cookies fácilmente:

req.setRequestHeader("Cookie", "someKey=true");

Una vez que el objeto está inicializado y listo para trabajar, lanzaremos la petición:

req.send(null);

El null vale para peticiones de tipo GET. En el caso de POST, se debe especificar los parámetros, por ejemplo:

req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded";
req.send("name=scott&email=stiger@foocorp.com");

Es aconsejable que la función que procesa el XML resultante tenga un control mínimo del resultado. El readystate 4 significa que la petición ha finalizado por completo y el status 200 es el OK del protocolo HTTP.

function processXMLResponse() {
  if (req.readyState == 4) {
    if (req.status == 200) {
     // Process the XML response
    }
  }
}

Para procesar la respuesta XML simplemente hay que utilizar las funciones de JavaScript DOM. Por ejemplo, para extraer el nombre del empleado del siguiente XML:

<employee>
  Chris
</employee>

Se puede utilizar lo siguiente:

var name = req.responseXML.getElementsByTagName("employee")[0];

Lógicamente, lo normal es recorrer árboles DOM un poco más complejos, utilizando por ejemplo bucles como el siguiente:

for (i=0;i<elements.length;i++) {
  for (j=0;j<elements[i].childNodes.length;j++) {
    var ElementData = elements[i].childNodes[j].firstChild.nodeValue;
  }
}

Y eso es todo. Espero que haya quedado claro cómo funciona Ajax de forma sencilla. Más adelante publicaré un ejemplo real de esta tecnología.

Integración contínua

mayo 8, 2006

Suelo leer los artículos que escribe Martin Fowler. La primera vez que leí algo suyo fue su libro sobre UML, que siempre recomiendo cuando me preguntan qué leer en esa materia. Me gustó su forma de explicar temas técnicos y su opinión pragmática sobre el mundo de la informática.

En este caso voy a hacer referencia a la última versión sobre el artículo de Integración Contínua que redactó hace ya bastante tiempo. Es un documento que reune una serie de buenas prácticas ya conocidas, pero que juntas forman la mencionada integración contínua. Yo empecé a tener contacto con éstas prácticas a finales del 99, cuando tuve oportunidad de trabajar en una verdadera factoría de software.

La esencia de esta práctica, una de las 12 originales del proceso Extreme Programming, es la de que cada desarrollador realice integraciones periódicas de su trabajo, al menos una vez al día, con un repositorio de código compartido. Como señala el autor del artículo, mucha gente cree que es un trabajo imposible de lograr y que realmente no aporta mucho: dos errores muy graves. Es muy sencillo de poner en marcha, con las herramientas de hoy en día es casi inmediato, y los beneficios son enormes. Detectar problemas de integración rápidamente ayuda a que el propio proceso de integración sea más rápido ya que los problemas no aparecen de a miles y que el código sea más estable. Como consecuencia de ello, también se acelera el proceso de pruebas.

El artículo propone las siguientes prácticas como parte de la Integración Contínua:

  • Mantener un único repositorio de código
  • Automatizar el proceso de compilación y generación de ejecutables
  • Integrar pruebas automatizadas dentro del proceso de compilación
  • Todos envían su código a diario
  • Con cada cambio de código, se debería lanzar el proceso de compilación en un entorno de integración
  • Mantener el proceso de compilación rápido
  • Realizar pruebas en un entorno igual al de producción
  • Obtener la última versión de los ejecutables debe ser fácil
  • Que todos puedan ver el estado actual del código
  • Automatizar el proceso de despliegue de las aplicaciones

La lista de prácticas que propone el autor es muy completa, pero no es necesario ponerlas en marcha todas a la vez. Yo propongo plantear una introducción paulatina, dependiendo de la madurez del equipo de trabajo y de las prácticas que ya se estén aplicando, hasta completarlas todas.

No más StringBuffer…

abril 26, 2006

En el JDK 1.4, el compilador transforma el operador binario + entre Strings a llamadas al método append de StringBuffer. Ya no es un problema concatenar muchos Strings con el + ó el +=.

Ejemplo:

Esto x = "a" + 4 + "c" se convierte en x = new StringBuffer().append("a").append(4).append("c").toString().

Se puede ver en el API de StringBuffer.

Cohesión y acoplamiento

abril 24, 2006

En la facultad cursé una asignatura llamada "Técnicas de Diseño". El equipo que llevaba la asignatura no era una gran autoridad en el tema del diseño de software, temática de la materia, y el responsable de la cátedra tenía muchos defectos, pero debo reconocer que era una persona sumamente convencida de sus ideas. Quizás más por amor propio que otra cosa. En cualquier caso, él estaba convencido de que la cohesión y el acoplamiento eran dos conceptos fundamentales en el diseño de soluciones informáticas. Y la verdad es que tiene toda la razón.

Voy a intentar definir cohesión y acoplamiento, desde el punto de vista informático. La cohesión es el grado de cercanía entre dos o más elementos. Podríamos decir que es un conjunto de características que los une y, por lo tanto, los agrupa bajo un mismo denominador, define su identidad. El acoplamiento, en cambio, mide el grado de dependencia entre dos o más elementos. Esto quiere decir que, según su dependencia, estarán más o menos acoplados. Mayor dependencia implica mayor acomplamiento.

El objetivo final del diseño de software (o soluciones informáticas) en materia de estos dos conceptos es: Reducir al máximo el acoplamiento entre componentes y aumentar la cohesión interna de cada componente.

¿Qué quiere decir reducir el acoplamiento? Básicamente, lo más importante es saber definir las responsabilidades de cada componente para garantizar que la dependencia sea funcional o arquitectónica, no de implementación. Un ejemplo simple de acoplamiento es cuando un componente accede directamente a un dato que pertenece a otro componente. ¿Qué ocurre en ese caso? Pues que el resultado del comportamiento del componente A dependerá del valor del componente B, por lo tanto, están acoplados. Cabe destacar que el acoplamiento no es malo, ojo. Simplemente hay que saber eliminar el acoplamiento que no sea funcional o arquitectónico. En el caso de acoplamiento funcional, está bien que un componente de cálculo de probabilidades dependa de un componente de cálculo matemático básico, ya que es evidente que para calcular probabilidades será necesario aplicar fórmulas matemátics y resolver operaciónes aritméticas. En el caso de acoplamiento arquitectónico, se puede ver un ejemplo en el esquema MVC (modelo-vista-controlador). La capa del modelo realizará cambios de acuerdo con los parámetros que recibe del controlador, por lo tanto hay cierta dependencia, pero sólo como separación de capas de una arquitectura claramente definida.

¿Qué ocurre en el caso de la cohesión? La cohesión de un componente mide la identidad de su comportamiento. El comportamiento de un componente se define mediante una serie de operaciones (contrato), cada una de ellas con una responsabilidad y función. La suma de todas las operaciones que definen dicho comportamiento integran una identidad. La cohesión no es buena cuando nos encontramos con que hay operaciones con un objetivo junto a otras con otro objetivo dentro de un mismo componente, cuya definición funcional es una y clara. Un ejemplo claro y sencillo de esto es el siguiente. Imaginemos un componente que gestiona el estado de una ficha de datos personales. Lo lógico sería que su contrato tuviese operaciones como "Actualizar nombres y apellidos", "Eliminar teléfono de contacto" y demás. Si defino una operación que calcula el pronóstico del tiempo en Tombuctú para el próximo fin de semana, pues la cohesión se anula completamente, ya que nada tiene que ver con el resto de operaciones y, sobretodo, con el fin del componente.

Con esto propongo un debate sobre estos dos conceptos, para enriquecernos entre todos y mejorar el uso que hacemos de ello a diario en nuestros diseños. Aunque no lo parezca, aplicamos estas ideas constantemente, en todos los niveles de un equipo de desarrollo de soluciones informáticas.

Un saludo para todos.

Bienvenidos a mi blog técnico

abril 24, 2006

Hola a todos,

He decido abrir un blog técnico donde comentar temas relacionados con el desarrollo de soluciones informáticas. Principalmente voy a escribir sobre la calidad en el diseño y desarrollo de software, intentando no meterme demasiado en temas de una u otra tecnología. Por una cuestión de afinidad personal, cuando hable de código seguramente utilizaré Java para demostrar ejemplos, pero haré el esfuerzo de ejemplificar con otros lenguajes de programación como ser Ruby, C, Smalltalk, PHP y quizás alguno de Microsoft.

Espero que los contenidos sean interesantes para todos y que podamos colaborar juntos para aprender un poco más sobre cómo se puede trabajar mejor en esta actividad, la del diseño y desarrollo de soluciones informáticas.

Un saludo para todos.