Inversión de Dependencias

Este el quinto principio de los principios SOLID y personalmente creo que es el que mayor impacto tiene a la hora de hacer soluciones testeables. Pero curiosamente me encuentro recurrentemente con una importante cantidad de gente que no lo usa o que lo interpreta incorrectamente. Es por esto que decidí hacer un video al respecto.

Me quedó un video bastante corto, ~10 minutos, en el que explico el principio utilizando código y diagramas. Espero les resulte útil.

Recursos sobre Persistencia Objeto-Relacional

Persistir Objetos en una base de datos relacional es una problemática muy común sobre todo en el desarrollo de las denominadas «aplicaciones enterprise». Los desafíos de esta cuestión han sido caracterizados con nombre propio: «Impedance Mismatch«. Hoy en día esta problemática ha sido resuelta por diversas en herramientas entre las que a mi entender se destacan Hibernate(Java) y ActiveRecord(Ruby).

En MeMo2 trabajamos con Ruby pero evitamos el uso de ActiveRecord de la manera tradicional pues resulta intrusiva en el modelo de objetos, ya que obliga a que las clases a persistir deban heredar de una clase de ActiveRecord. La intención en MeMo2 es implementar lo que desde una perspectiva Domain-Driven Design suele denominarse «Persistence Ignorance», esto es que el modelo de objetos permanezca «ignorante» de la tecnología de persistencia. Si bien es importante que los alumnos entiendan los desafíos de esta problemática, no esperamos que implementen un ORM pero si que respeten la idea de «Persistence Ignorance». Es por ello que a lo largo de varios cuatrimestres hemos provisto a los alumnos con ejemplos de implementación de persistencia basados en la idea de Repositorios y utilizando distintos frameworks. Basados en el feedback que los propios alumnos nos han dado, este cuatrimestre agregamos un ejemplo de implementación basado en Sequel y una serie de video que explican la problemática y algunas técnicas de diseño.

El primer video explica conceptual el problema de guardar objetos en una base de datos relacional.

El segundo video explica desde los distintos modelos con los lidiamos en el desarrollo de una aplicación y como es la relación entre ellos a la hora de implementar la persistencia.

Finalmente, el tercer video menciona una seria de recomendaciones para implementar la persistencia de forma trasparente utilizando el patrón Repositorio.

Situaciones de Diseño #1

Hace tiempo venia con la idea de publicar una serie de videos sobre diversas técnicas/patrones/recomendaciones para lidiar con situaciones de diseño habituales. Finalmente ayer decidí poner manos a la obra y publiqué el primer video de esta serie.

La situación que aborda este primer video es cómo lidiar con lógica que depende de fechas de una forma testeable. Muchas veces cuando tenemos lógica que depende de la fecha actual, estamos tentados de utilizar directamente una llamada al componente nativo de fecha. El problema con este camino es que no resulta testeable. La clave está en encapsular el acceso a la fecha del sistema de manera tal de poner utilizar un doble de prueba (mock) en el momento de hacer nuestra pruebas automatizadas.

Les dejo aquí el video y los invito a que me dejen en los comentarios alguna otra situación que les gustaría que aborde en próximos videos.

Sobre por qué la programación orientada a objetos perdió relevancia

Erase una vez cuando se desarrollaban aplicaciones grandes, con decenas o incluso cientos de entidades. Era una época donde se esperaba que cada pieza de software viviera por muchos años y los ciclos de release podían extenderse por muchos meses excediendo incluso el año. Era una época donde la programación orientada a objetos y sobre todo un buen modelo de objetos podía marcar una diferencia relevante en la construcción, evolución y mantenimiento de una pieza de software[1].

Pero esa época ha quedado atrás, cada vez son más las demandas del negocio por dar respuestas rápidas a las situaciones cambiantes del mercado. Los ciclos de release se han acortado radicalmente y de mano de ello también alcance de cada aplicación. Los avances en virtualización y la popularización de los contenedores han habilitado el surgimiento de prácticas como infraestructura como código y continuous delivery. En este contexto, propuestas de diseño como la de microservicios han probado ser muy efectivas. Cada microservicio es una aplicación de un tamaño acotado, no hay un regla fija de esto pero se suele usar como referencia un bounded context [2]. En este hilo de razonamiento cada aplicación no tiene ya tantas entidades y tampoco se espera que un microservicio «viva por siempre». Dado que los microservicios son relativamente pequeños no resulta impensado que luego de un tiempo sea reemplazado por una nueva versión que podría estar incluso desarrollada con una tecnología distinta.

Esto me lleva a pensar que en cierto modo la importancia de la programación orientada a objetos ha perdido cierta importancia relativa. En un punto cuestiones como la arquitectura de runtime (que impactan en la posibilidad de escalar) han tomado mayor relevancia (posiblemente porque la necesidad de escalar está mucho más presente ahora que hace 15 años).

Sin embargo estoy convencido que los principios de la orientación siguen completamente vigentes porque muchos de ellos son aplicables más allá de la programación. De hecho, los principios de diseño de los microservicios no son más que «refraseos» de los principios de orientación objetos. Un caso es el principio de Responsabilidad Única (Single Responsibility Principle): un objeto debe hacer UNA cosa y hacerla bien, eso mismo aplica a los microservicios. De hecho creo que perfectamente uno podría tomar los principios de orientación a objetos, aplicarlos al diseño de microservicios y luego programar internamente el microservicio utitilizando objetos pero de una forma más «procedural» utilizando alguna estrategia del Transaction Script. en lugar de hacer un Domain Model.

Tal vez este razonamiento genere alguna crítica y ante ello debo aclarar que he estado enseñando Programación Orientada a Objetos por más de 15 años y aún lo hago. Pero desde hace un tiempo vengo con la sensación de que en la medida en que podamos hacer aplicaciones/servicios pequeños, con pruebas automatizadas y prácticas de configuración management, la forma interna en que programemos las aplicación, resulta menos relevante.

[1] Posiblemente en algunos contextos esto siga ocurriendo
[2] Bounded Context es una idea desarrollada por Eric Evans en su libro Domain-Driven Design

El desafió de Enseñar a Diseñar

El desafió de Enseñar a Diseñar

Desde mi comienzo en la carrera docente siempre he estado en materias que en mayor o menor medida han incluido la temática de diseño de software. Cuando estaba en Algo3 y AyDOO era diseño a nivel de clases, ahora en MeMo2 cubrimos un poco de diseño a nivel de clases y también diseño de más alto nivel (arquitectura e infraestructura). Enseñar a diseñar es una tarea que me parece muy difícil. De entrada debemos decir que no hay diseños «malos» o «buenos», sino que un diseño podrá ser más o menos conveniente dependiente del contexto. Un diseño es consecuencia de un conjunto de decisiones, diseñar es decidir, elegir. Pensando en el proceso de diseño veo al menos 3 pasos:
  1. Entender el contexto, el problema a resolver, las restricciones y necesidades. Esto es lo que Kazman denomina Architectural Drivers.
  2. Entendido el contexto hay que identificar las decisiones relevantes que deben tomarse.
  3. Finalmente para cada decisión relevante hay que analizar las posibles alternativas y elegir una. Esto puede implicar hacer algunas pruebas de concepto para poder tomar decisiones con evidencia que las respalde.
El punto 1 cae dentro del área denominada ingeniería de requerimientos y cuenta con un amplio cuerpo de conocimiento. El punto 2, ya es más complejo y más allá del cuerpo de conocimiento requiere de experiencia, lo cual es bastante difícil de transmitir. El punto 3 también reviste de complejidad pero creo que en general es más fácil que el punto 2. A mi parecer identificar opciones y evaluarlas para decidir es una trabajo en esencia muy ingenieril. A comienzo de año estábamos hablando con mi colega docente Diego Marcet sobre los criterios de corrección de un ejercicio de AyDOO y lo vimos:
Pretender que en un cuatrimestre académico alguien aprenda a diseñar es demasiado ambicioso, o sea, se puede enseñar algún método/heurística pero pretender dominarlo y generar buenos resultados puede ser difícil. Por ello ajustamos nuestro objetivo para centrarnos en dar a los alumnos herramientas para ser capaces de detectar las decisiones importantes y al mismo tiempo que sean capaces de detectar decisiones inapropiadas para un contexto dado.
De cara a este objetivo trabajamos bastante sobre código ajeno, o sea, analizando y extendiendo código existente. Continuará….

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