Una alternativa al Double Dispatch

Double Dispatch es un patrón de diseño para resolver situaciones en las que el comportamiento resultante no depende solamente del objeto que recibe el mensaje sino también de parámetro enviado en ese mensaje. Veamos un caso concreto para entender mejor esta situación.

Supongamos que debemos modelar un juego de naves espaciales tipo Galaga donde tenemos distintos tipos de objetos espaciales como ser Naves, Estaciones y Asteroides. Estos objetos pueden colisionar entre si y el resultado de dicha colisión depende particularmente de quienes son los objetos involucrados y del estado de los mismos. Veamos un ejemplo:

  • Si una nave colisiona con un asteroide la nave es destruida (vida = 0) y el asteroide disminuye su velocidad en 3 unidades
  • Si una nave colisiona con una estación a baja velocidad (velocidad < 10), entonces la nave se detiene (velocidad = 0) y la estación no sufre ningún cambio de estado
  • Si una nave colisiona con una estación a alta velocidad (velocidad >= 10), entonces la nave es destruida (vida = 0) y la estación disminuye su vida en 4 unidades.

Una solución trivial podría ser que cada objeto tenga un método “collideWith” en el que se verifique contra quien está chocando y se actúe en base a ello:

// SpaceShip
public void collideWith(SpaceObject otherObject) {

    if (otherObject.getClass() == Asteroid.class) {
        this.life = 0;
    }

    if (otherObject.getClass() == Station.class) {
        if (this.speed < 10){
            this.speed = 0;
        }
        else{
            this.life = 0;
        }
    }
}

Código completo Java de esta solución disponible aquí.

spacewar_trivial

Las clases Asteroide y Estación tendrían métodos análogos.
Si bien esta solución funciona, resulta un poco “rústica” en términos de Orientación a Objetos porque viola algunos principios de diseño, entre ellos viola el principio abierto-cerrado, ya que en caso que aparecer un nuevo tipo de objeto (como por ejemplo Cometa) habría que modificar cada uno de los métodos CollideWith para contemplar los nuevos casos.

Es aquí donde el Double Dispatch nos propone una alternativa un poco más elegante. La idea es que cuando un objeto recibe una colisión, delega la resolución al otro objeto invocando a un método más concreto como se muestra en el siguiente fragmento de código:

// SpaceShip
// Dado que aquí "this" es SpaceShip, se terminará ejecutando
// el método collideWith(SpaceShip) del otherObject
public void collideWith(SpaceObject otherObject) {
 otherObject.collideWith(this); 
}

protected void collideWith(Asteroid asteroid) {
 this.life = 0;
}

protected void collideWith(Station station) {
 // do something
}

protected void collideWith(SpaceShip ship) {
 // do other something
}

Código completo Java de esta solución disponible aquí.

spacewar_doubledispatch

Esta solución implica que cada clase tendrá un método collideWith por cada clase con la que pueda llegar a colisionar. En este caso particular donde tenemos 3 clases, tendremos 9 métodos (dependiendo del comportamiento particular que definamos para resolver la colisión podríamos tener tal vez algunos métodos menos).  En cierto modo esta solución también viola el principio abierto-cerrado, porque en caso de aparecer un nuevo tipo de objeto es necesario modificar todas las clases para agregar un nuevo método que resuelva la colisión aunque a diferencia de la solución anterior, aquí no modificamos un método existente sino que agregamos un nuevo método, lo cual es menos rústico.

Finalmente la solución a que mi gusta para esta situación entra en la categoría que Steve McConnell denomina Table-Driven methods (capítulo 18 de libro Code Complete). La idea es emular una tabla de métodos virtuales que es lo que usan algunos lenguajes a bajo nivel para resolver el late binding. Más concretamente lo que se hace es que cada objeto tenga una tabla o mejor dicho un diccionario (collisionMap) donde la clave es la clase contra la cual colisiona y el valor es un closure/lamba con la lógica a ejecutarse al colisionar contra ese tipo de objeto. Implementar esto en Smalltalk y/o Ruby es trivial, pero en Java tiene una vuelta de rosca extra, simplemente por la forma en que se implementan los lambdas en Java.

// Cuando se crea el objeto debe inicializarse el collitionMap
protected void initCollisionMap() {
    collisionMap = new HashMap<>();
    collisionMap.put(Asteroid.class, (x) -> collideWithAsteriod(x));
    collisionMap.put(Station.class, (x) -> collideWithStation(x));
}

// Este el punto de entrada en la colision
// Notar que la busqueda en la tabla es polimorfica
public void collideWith(SpaceObject otherObject) {
 CollitionHandler handler = this.collisionMap.get(otherObject.getClass());
 handler.collideWith(otherObject);
}

private void collideWithAsteriod(SpaceObject other) {
 this.life = 0;
}

private void collideWithStation(SpaceObject x) {
   // do something
}

Código completo Java de esta solución disponible aquí.

spacewar_table

A la hora de agregar un nuevo tipo de objeto, esta solución requiere agregar una nueva entrada al collisionMap y un nuevo método si es que la interacción con este nuevo tipo de objeto es distinta a las ya existentes. Otro punto interesante de esta solución es la posibilidad de variar el comportamiento de cada instancia de la misma clase, ya que el collisionMap puede alterarse en tiempo de ejecución.

Continuará…

Preparando Análisis y Diseño Orientado a Objetos en UNTreF

Por estos días me encuentro preparándome para dictar esta materia el primer cuatrimestre de este año. Si bien ya he dictado este materia en otras ocasiones esta vez tengo el desafío de dictarla solo, las veces anteriores la dicté en conjunto con @dfontde. En base al feedback obtenido de las dictadas anteriores y de algunas ideas que probando en otras materias, he decido hacer algunos ajustes a la dinámica de dictado de la materia.

El avance de internet, las redes sociales y la sobrecarga de información requieren que la dinámica de las materias se adapte, por ello más allá de la clase presencial semanal tendremos un modelo de aula extendida basado en un plataforma web. En base a esto se espera que los alumnos tengan una interacción constante con todo el grupo de estudio. Por cada hora de clase presencial se espera una dedicación de al menos una hora de trabajo fuera del aula. Dicho esto y pasando en limpio: los alumnos que cursen la materia deberán dedicar al menos 8 horas semanales todas las semanas. Dependiendo de cuanta maña pueda darse cada alumno, puede que la materia le insuma unas 6 horas semanales o puede que le insuma unas 10. El punto clave aquí es que a diferencia de otras materias no es posible llevar esta materia “haciendo la plancha” y estudiando a 30 horas seguidas los días previos a la evaluación. Se quiere una dedicación constante.

Respecto del mecanismo de evaluación no me gusta tomar examen escrito (aunque no lo descarto) y por ello estoy diseñando un mecanismo de evaluación basado en tareas semanales. La mayoría de estas tareas será de índole individual. Las tareas consistirán principalmente en lecturas con cuestionarios asociados y resolución de problemas de modelado y programación.

Sobre el TP final de algo3 (2015-1)

A fines de mayo lanzamos el TP final de Algo3. En el curso de los miércoles tenemos poco más de 30 alumnos repartidos en equipos de 3 integrantes, cada equipo con un docente tutor. En mi caso soy tutor de 4 equipos.

Este cuatrimestre el trabajo prácticos se llama AlgoCraft y como su nombre lo sugiere es una variante del clásico juego StarCraft.

Como de costumbre desde el comienzo del trabajo práctico configuré un Jenkins para que los alumnos pudieran hacer integración continua. Esto también me permite tener métricas de su trabajo. Este cuatrimestre incorporé PMD al conjunto de tareas ejecutadas en el proceso de integración continua. PMD es un herramienta que entra en la categoría de “Linter” y como tal, lo que hace es analizar el código fuente realizando un conjunto de verificaciones relacionadas al cumplimiento de estándares y buenas prácticas de programación del lenguaje en cuestión.

algo3_20151

Preparando mi visita a la Universidad Piloto (Colombia)

La semana próxima (11 de Mayo) estaré de visita en la Universidad Piloto de Colombia (Girardot) para participar del V Foro Internacional en el contexto de la celebración de los 30 años del Programa de Ingeniería en Sistemas en la región.

Una de las actividades que realizaré durante mi visita será una Clínica de Programación Orientada a Objetos. Es la primera vez que voy a realizar esta actividad como tal. La misma reúne varias cuestiones que son parte del temario de las materias que suelo dictar y que estarán incluida en mi próximo libro sobre el tema.

Quiero agradecer especialmente a Elkin Forero y a Luis Carlos Garzón por la invitación

clinica_oo

 

Experiencias de Enseñanza de POO en WISIT 2014

El sábado pasado estuve participando del WISIT 2014. Junto con Pablo Suárez presentamos el enfoque estamos utilizando en FIUBA para enseñar Programación Orientada a Objetos.

En nuestra sesión destacamos 4 puntos que consideramos centrales en nuestro enfoque:

  • Uso de técnicas de educación centrada en el alumno (Learner Centered Teaching)
  • Uso de herramientas informáticas: Campus Virtual de la universidad, Foros, Sistema de gestión de TPs (alfred) y videos explicativos.
  • Uso de dos lenguajes: Smalltalk y Java
  • Test-Driven: no solo enseñamos y usamos TDD, sino que también el desarrollo de los trabajos tiene algo de TDD pues las especificaciones de los que los alumnos deben resolver, la entregamos siempre en forma de pruebas.

Creemos que la presentación salió muy bien y notamos a la audiencia muy interesada. De hecho al finalizar nuestra exposición recibimos varias consultas y más de una persona manifestó intenciones de probar Alfred.

Para facilitar la sesión sesión utilizamos un Prezi que armó Pablo y que está disponible aquí. También armamos este póster que enviamos en su momento a los organizadores del evento como parte de nuestra propuesta de sesión.

Curiosamente hubo otras dos sesiones en las que también se presentaron enfoques de enseñanza de POO. Una de esas sesiones estuvo a cargo de Alfredo Sanzo y Lucas Spigariol quienes contaron su enfoque fuertemente basado en actividades de representación/actuación y en el uso de objetos físicos.

La otra sesión sobre POO estuvo presentada por Nico Passerini, Javi Fernández y Pablo Tesone, quienes mostraron Wollok, una herramienta basada en Eclipse y un lenguaje desarrollado por ellos mismo con el fin específico de enseñanza de POO.

Ambos enfoques me parecieron muy interesantes.

Celebro la iniciativa Uqbar Project de llevar adelante este evento. ¡Que se repita!

Probando excepciones

¿Como probar que un método lanza una excepción ante una determinada situación excepcional? Usando NUnit o JUnit 4, basta con escribir el método de prueba y poner una simple anotación indicando el tipo de excepción esperada.

@Test(expected = ExceptionEsperada.class)  
public void xxxxxx(){
      // ejecutamos la código que debiera lanzar la excepción
}

Pero no todos los xUnit brindan esta posibilidad, entonces debemos apelar a una estrategia similar a la siguiente.

public void test_xxxxxx(){
     try{
         // ejecutamos la código que debiera lanzar la excepción
         fail(); // si ejecución llega hasta esta línea, entonces significa que no se lanzó la excepción esperada y por ende la prueba ha fallado
      }
      catch(ExceptionEsperada ex){
          /* si estamos aqui, entonces se ha producido la excepción esperada y no es necesario
             hacer nada pues a menos que se indique que algo ha ido mail, se asume todo estuvo 
             bien */
       }
       catch(Exception ex){
          // si estamos aqui, es que la excepción que se ha lanzado, no es la esperada, por lo tanto la prueba ha fallado
          fail();
       }
    }

Asi de simple, espero les resulte útil.

Escribiendo pruebas unitarias para código legacy

Desde que volví a trabajar en consultoría este es uno de los temas que más me he encontrado. Sinceramente no me sorprende pues:

  • TDD y la automatización de pruebas, son dos temas que están en claro ascenso de popularidad
  • Casi toda la bibliografía de TDD parte de la base de la creación de aplicaciones desde cero
  • Pero en una porción importante de casos, la gente ya cuenta con una aplicación, la cual muchas veces no ha sido diseñada de forma de facilitar su prueba

Casualmente ayer me crucé con un caso extremo. Resulta que hace un par de semanas dicté un workshop de TDD. Ayer me contactó uno de los asistentes para hacer una consulta de como encarar un caso concreto: tenia que agregar una funcionalidad a la una clase existente. La clase en cuestión tenia más de 3000 líneas de código. Si si, leiste bien, son tres ceros después del tres, o sea, tres mil líneas de código. El problema de una clase tan grande, es que resulta dificil que sea cohesiva. Se supone que pasar ser cohesiva, una clase debe hacer UNA cosa y hacer bien. Con tantas líneas, es muy posible que dicha clase esté haciendo demasiadas cosas.

Pero el problema que motivaba la consulta no eran las 3000 líneas de código, sino el constructor de dicha clase. Resulta que el constructor además de recibir varios parámetros, instanciaba un componente para conectarse a la base de datos. Eso impedia realizar una prueba unitaria de la clase, pues el solo hecho de instancialar implicaba conectarse a la base. Tampoco no habia chances de mockear la conexión pues era instanciada directamente dentro del constructor. ¿que hacer entonces?

Lo primero que uno intentaria es hacer un refactoring de la clase, pero dado que no existian pruebas de la misma, cualquier modificación implicaba un gran riesgo. Luego de analizar varias alternativas llegamos a una solución de compromiso, que nos permitiria escribir tests unitarios: agregar un constructor sin parámetros, que tenga la lógica para lanzar un excepción si era invocado en un ambiente distinto al de desarrollo/test. De esta forma podriamos instancias la clase en forma aislada en un ambiente de test y  aseguramos que no sea instanciada de esta forma en caso de estar en un ambiente distinto.