17 febrero 2008

ACM Interactions

Por alguna alineación especial de los astros el volumen de Enero/Febrero del 2008 de ACM Interactions (la publicacion de ACM dedicada a interaction design) esta disponible gratis para bajar (las revistas de ACM por lo general requieren subscripción).
Bueno antes de que me olvide aca va el link. (en el lector online de la revista hay una opción para bajar el PDF)

Más sobre JUnit 4.4

En un post anterior, comente las nuevas caracteristicas de JUnit 4.4.

Ahi mecione que no le veia demasiado valor a los "Theories" que incorporó el framework. Debo adminitir que me equivoqué. Los estuve probando y ahora les veo dos cosas muy interesantes.

La primera es que es tipico tener casos donde hay una interfaz, luego una clase abstracta que implementa ciertos metodos y finalmente varias implementaciones. El problema que surgue con este esquema es: ¿Donde testeo los metodos que implementa la clase abstracta?. Mi solución a este problema siempre fue: o bien repetir los tests de estos metodos en los tests de unidad de cada clase concreta o bien usar un test abstracto y heredar los tests de las clases concretas de este test abstracto.

Los "theories" resuelven este problema de una forma muy elegante: cada clase concreta puede ser un "DataPoint" de un test con theories, y problema solucionado. Cuando se agrega una nueva clase concreta solo hay que agregarla como "DataPoint" (es una lastima que el meta modelo de Java no soporte el "allSubclasses" de Smalltalk).

Otra caracteristica que encontre importante es la de comunicación, usando la palabra "Theory" en lugar de test, se transmite muy bien la intención de "Programming as theory building".

Los unicos problemas que le veo a JUnit 4.x son: que todavía no tiene una buena integración con Eclipse, y que el uso excesivo de los import static es muy incomodo (estoy acostumbrado a usar organize imports asi que cada vez que tengo que usar uno de estos metodos estaticos que no use antes tengo que volver a escribir el import, horrible).

08 febrero 2008

Los prefijos "set" y "get" en Java son un dolor de bolas

Canilla

¿Para que lado se gira una llave de paso de una canilla... izquierda o derecha?

Es difícil responder a esta pregunta de forma instantánea. Uno se tiene que poner a pensar en como usa canilla, y quizás hasta hacer el gesto con la mano de como se gira la llave de paso. Sin embargo cuando nos vamos a lavar las manos, lo hacemos de forma inmediata, y ni siquiera tememos que pensarlo.

¿Por que pasa esto?

Por que la canilla en si misma tiene la información para utilizarla. Según Don Norman, en su libro “The Design of Everyday Things”, todas las cosas que usamos diariamente contienen una cantidad enorme de información. Información que no nos interesa retener de forma exacta en la cabeza, pero que tenemos presente a la hora de utilizarlas.

Es por eso que desde el punto de vista de usabilidad, las cosas bien diseñadas no requieren un manual de uso: el mismo objeto nos da información de como utilizarlo.

¿Y de que forma se nos presenta esta información?

Siguiendo con el libro de Norman: esta información se nos presenta por medio de affordances y restricciones (constraints).

En el caso de la canilla, la misma forma nos incita a girarla (affordance) y es la rigidez de la llave de paso nos dice: “para este lado no abre... proba para el otro” (constraint).

Estos constraints pueden ser físicos, como en el caso de la canilla. O bien culturales.

A simple vista pareciera que los constraints culturales son más “leves”, sin embargo pueden tan fuertes como los físicos, por ejemplo si tengo un muñeco para armar sé que la cabeza tiene cierta dirección (a menos que este armando un modelo de la chica de Poltergeist).

Playmobil

En los lenguajes de programación pasa mas o menos los mismo. Hay constraints “físicos”: si no uso la sintaxis correcta el programa no compila. Y constraints culturales, por ejemplo en Smalltalk o Java nadie usa nombres de clase en minúsculas.

Este blog post parte de la hipótesis de que en programación estas restricciones culturales a veces pueden influir en la forma en que pensamos los problemas.

Por ejemplo en Smalltalk, se suele tener la convención “cultural” de hacer que los métodos tengan nombres cuya escritura sea similar a un texto en ingles, por ejemplo:

client loginWith: credentials toServerAt: address.

En cambio si uno ve cosas de este estilo:

client setCredentials: credentials; setServerAddress: address; login.

Suena raro. Por lo que esta convención tiene un impacto en como esta diseñado el framework: uno pone más énfasis en la comunicación de la intención, que en los detalles de implementación.

Hecha toda esta introducción voy a ir al tema que le da titulo a este post.

JavaBeans

Entre las las “restricciones culturales” que existen los lenguajes de programación, identifico dos grupos:

  • Las que provienen de hacer mas legible el código
  • Las que provienen de una cuestión de implementación

En el caso de Java, entran en el primer grupo cuestiones como: las clases van en CamelCase, los paquetes en minúsculas, los métodos en minuculasMayusculas sin usar “_” como separador de palabras, etc.

Y en el segundo grupo cuestiones como poner el prefijo “set” o “get” si el método es un accessor a una variable de instancia.

Este uso de “set” y “get” se lo debemos principalmente al framework de JavaBeans.

Para los que no conozcan el framework de JavaBeans, les cuento un poco la historia:

Con la aparición de editores gráficos, muchas veces se hace necesario contar con metadata que permita manipular a los objetos gráficamente. El problema de esta metadata es que agrega una complejidad que suele ser bastante molesta: la metadata es importante para el framework, pero no para el problema que tratamos del modelar.

Por esta razón los diseñadores del framework, separaron la metadata de los objetos que modelan el problema. En el caso de JavaBeans, un editor que manipula objetos gráficamente puede utilizar la metadata proveniente de una clase que implementa BeanInfo.

Esta clase entre otras coas posee información sobre que atributos se pueden manipularse gráficamente.

El problema es que crear estas clases BeanInfo es bastante molesto. Por lo que los diseñadores del framework agregaron un shortcut: si no hay BeanInfo se asume que la clase sigue una convención de nombres (el “set” y “get”) para saber que métodos se utilizan para modificar el estado del objeto.

Luego la misma metadata fue utilizada para paginas web, con la intención de hacer editores gráficos. Y así fue como la convención de “set”, “get”, “is” se empezó a utilizar en todos lados. Hasta que finalmente, no importa si un objeto va a ser manipulado gráficamente o no: todos los desarrolladores en Java usan la convención del prefijo “set”/”get”.

Consecuencias del “set”/”get”

Sin embargo esta convención tiene sus consecuencias: uno empieza a pensar en los objetos como si fuesen estructuras de datos potenciadas.

Cualquier fanatico de objetos diria: un objeto no es una estructura de datos con métodos!

Pero entonces... ¿Cual es la diferencia?

Bueno desde el punto de vista de implementación uno puede ver a los objetos como si fuesen estructuras de datos con métodos. Al igual que uno puede ver a todos los sistemas computacionales como una gran maquina de estados.

El problema no es que este punto de vista sea erróneo, el problema es que no es un punto de vista útil para diseñar un sistema orientado a objetos. Ocurre lo mismo con las maquinas de estados: plantear todo sistema como maquina de estados es útil para utilizar herramientas de model checking, pero hace que el sistema sea más difícil de entender por un humano. (es análogo a lo que ocurre en matemática: si uno se pone a demostrar todo con una formalidad lógica exacta el problema se hace mucho más difícil de manejar).

Voy a mostrar lo que comenté con un ejemplo, si uno ve este código:

display(directory.getFiles());

Uno lee: “mostrar, directorio obtener archivos”.

En cambio si no ve:

display(directory.files()).

Uno lee literalmente: “mostrar archivos del directorio”.

La diferencia es sutil, pero ese “get” en el medio hace que uno piense en el objeto directorio como si fuese una estructura de datos con una variable files adentro.

A lo que quiero llegar con este largo post es que habría que eliminar de una vez por todas la convención de “set”/”get” de Java!

No aporta mucho desde el punto de vista de comunicación ya que es muy fácil por contexto darse cuenta si un método cambia el estado de un objeto, y resulta perjudicial para plantear problemas desde el punto de vista de orientación a objetos.

Fotos:
La foto de la canilla fue obtenida de esta dirección http://flickr.com/photos/cwalker71/1021798486
Y la del playmobil de: http://flickr.com/photos/eleftheriag/370683078
Ambas tienen licencia Creative Commons, y pertenecen a sus respectivos autores.

04 febrero 2008

JUnit 4.4 y Hamcrest

A partir de la versión 4.4 de JUnit se incorporo al framework un cambio que permite expresar los assertions de una forma más natural. Tomando algunos ejemplos del release notes:

assertThat(x, is(3));
assertThat(x, is(not(4)));
assertThat(responseString, either(containsString("color")).or(containsString("colour")));
assertThat(myList, hasItem("3"));

Este cambio me pareció más que interesante. Primero por que la consecuencia de modelar ciertos conceptos muy abstractos como el chequeo realizado en un assert (llamado Matching en el framework) se obtuvo una interfaz con más "intention revealing" e información. Por ejemplo este tipo de assert puede mostrar mejores mensajes de error:

assertThat(responseString, anyOf(containsString("color"), containsString("colour")));
// ==> failure message:
// java.lang.AssertionError:
// Expected: (a string containing "color" or a string containing "colour")
//      got: "Please choose a font"

La otra consecuencia es que permite expresar más facilmente ciertos asserts que antes eran complicados, por ejemplo para chequear si una colección tiene ciertos elementos puede usarse directamente hasItems donde antes habría que crear una colección a parte y chequear por la inclusión de los elementos (o hacer un assert por elemento).

La buena noticia es que estos asserts especiales pueden utilizarse en versiones más viejas de JUnit ya que fueron "robados" de un proyecto llamado Hamcrest (ver tutorial).

Llevando la misma idea a Smalltalk

Si bien muchas expresiones son más simples en Smalltalk (debido al tipado dinamico y a los keyword messages) algo de este estilo:

assertThat(x, is(not(4)));

No queda de una forma linda:

self assertThat: x matches: (self is: (self not: 4))

Al principio pensé que aprovechando que en Smalltalk se puede "capturar" un DNU, podrían obtenerse expresiones similares. Por ejemplo al capturarse los DNUs para los selectors #assertThat:xxx:yyy:, determinar que Matcher utilizar en base a la porción "xxx:yyy:" del selector, luego el código quedaría asi:

self assertThat: x isNot: 4

Pero como podrán ver en el ejemplo en este caso el isNot no es una composición de Matchers, ouch!. (de todas formas estimo que is(not(4)) es equivalente a not(4)... si no la composición no me cierra, esta forma de hacer el "is not" debe ser solo por motivos de intention revealing, aunque no estoy con un Eclipse para poder comprobarlo).

La primer opción de crear los objetos directamente como en Java da un resultado que no queda tan expresivo, aunque es más simple y más flexible. 

Otra opción muy ala Smalltalk sería agregar metodos a Object para crear los Matchers (en lugar de usar la igualdad común y corriente), por ejemplo:

self assertThat: (x is: 4) negated

El problema de esta opción es que estos métodos para los Matchers tienen sentido solo en el contexto de los tests, por lo tanto agregarlos a Object sería una mala idea.

En fin, este tipo de "DSL Interno" como les llama Fowler (aunque yo prefiero llamarlos "truquitos sintacticos") tiene su particularidad en cada lenguaje. 

En Smalltalk las expresiones utilizando keyword messages y mensajes en cascadas son muy "lindas" para este tipo de cosas, pero en Java también los metodos static en conjunción con el "peligroso" import static, permiten usar Factory Methods para escribir expresiones "lindas".

La cuestión esta en si este tipo de assert agrega valor en Smalltalk. En Java yo creo que agrega mucho valor ya que la sintaxis del lenguaje hace dificil escribir cosas intention revealing y este me parece un lindo "hack" para lograrlas.

Otras caracteristicas de JUnit 4.4

Volviendo a JUnit, otra caracterisca robada de otro framework, es la capacidad de definir ciertos objetos que aplican a ciertos test (es decir definir explicitamente los "fixtures").

Por ejemplo es muy común escribir tests del estilo:

public void testResultWhenUserIsNotLoggedIn() // ....
public void testResultWithManagerUserSession() // ...
public void testResultWithGuestUserSession() // ...

No se si se capta la idea: cada test es para ciertos objetos (en este caso podrían ser sesiones de usuarios en diferentes roles).

Esta nueva caracteristica de JUnit, con el pomposo nombre de "Teorías", permite definir explicitamente los objetos y luego indicar mediante un "assumeThat" para caso aplica el test, seria algo asi:

@DataPoint private static Session nullSession = new NullSession();
public void testResultWhenUserIsNotLoggedIn(Session s) {
  assumeThat(session.notLoggedIn());
  // ....
}

Es decir los assumeThat actuan como predicados que indican que información corresponde al test.

Evidentemente esta caracteristica provee más información estatica sobre el test: un editor puede saber que "datos" se utilizan como entradas de los tests. 

Aunque no veo esta caracteristica con tanto entusiamo, me da la impresión que agrega mucho más complejidad al framework y no es tán facíl darse cuenta para que casos aplica cada test. Además por lo que vi en los ejemplos pareciera que las "Teorias" no están reificadas, eso genera que se usen anotations para cosas que se expresarian mejor de la manera "clasica".

Estos son mis "prejucios" sobre los Theories en JUnit 4.4... sería bueno ver comentarios de como esto funciona en un proyecto con muchos tests, o con "fixtures" complicados (los ejemplos en el sitio usan fixtures muy simples, la mayoría con tipos primitivos).