09 noviembre 2011

Microwiki: Primeros pasos de diseño

No importa que hable sobre principios y patrones de diseño, sobre polimorfismo, o si el ejemplo que di fue bueno o malo; al momento de la practica quien recién comienza aprendiendo diseño orientado a objetos se siente perdido y quiere una especie de guía que le diga que esta bien o mal.

El secreto es que incluso con más experiencia, uno tambien se encuentra algo perdido cuando se empieza con un problema nuevo.

Creo que un buen consejo para encarar un diseño es tener una "mente de principiante" y no dejar de preguntarse "¿Por qué?".
Claro que para hacerse las preguntas y poder responderlas se necesita un conocimiento previo.
Ambas cosas -hacerse las preguntas y construir ese conocimiento- van de la mano.

Esta introducción, viene a cuento que me dieron ganas de contarles las preguntas que me fui haciendo en la construcción de microwiki.

Para quienes no hayan visto mi post anterior, microwiki, es un pequeño servidor wiki que comencé a programar en mis ratos libres. Como objetivos de diseño, microwiki tiene las siguientes características:
  • Se utiliza localmente: no hay usuarios, ni permisos, ni historial de versiones en las paginas. 
  • Iniciar el servidor debe ser tan simple como ejecutar un comando.
  • Las paginas se guardan en el file system, y pueden editarse tanto dentro como fuera de la aplicación web.
  • La búsqueda de contenido debe ser rápida.


Las cuestiones técnicas

En un mundo teórico ideal, uno debería evaluar la funcionalidad, los atributos de calidad, ver las herramientas disponibles, un largo etcéra y luego elegir la solución técnica más adecuada. La realidad es mucho más simple, venia jugando un poco con Groovy y Gradle así que esas fueron las herramientas que elegí para trabajar.

Al principio pensé en hacerlo con Scala para practicar un poco este lenguaje, pero la comodidad de IntelliJ IDEA para usar Groovy me compro -me estoy volviendo viejo, ya no tengo ganas de ponerme a configurar plugins en versión beta.

El resto de las opciones fueron más simples. Conocía la sintaxis Markdown de usar GitHub y Stackoverflow, y PegDown fue el primer parser que encontré para Java.

Y si voy a hacer un pequeño web server tampoco iba a empezar de cero, Jetty es muy conocido por proveer un API simple para crear web servers en Java sin meterse con todo el lío de JEE (otra opción en Grizzly, pero es mucho más nuevo y no tiene tanta documentación).


Primeros pasos

Partiendo de que microwiki es una aplicación web, y que voy a utilizar servlets con Jetty, el primer paso fue pensar en un servlet que mostrara una pagina.
Consejo: Empezar a diseñar siempre por un caso particular y simple.
Entonces tenemos nuestro servlet que muestra la pagina. ¿Implementamos en el servlet la funcionalidad de abrir el archivo e invocar al parser? Respuesta rápida: no.


¿Por qué? El servlet se encarga de manejar el request y response de http, si ponemos todo junto no hay forma de testear la funcionalidad de obtener y parsear una pagina por separado.

Probablemente en alguna clase sobre diseño orientado a objetos escuchaste que las clases deberían tener alta cohesión, se referían justamente a este tipo de casos: hacer que la clase servlet implemente dos funcionalidades distintas es contraproducente a la hora de introducir cambios.

Los tests de unidad son útiles para detectar este tipo de problemas: si dejamos todo junto para testear la responsabilidad de brindar una pagina vamos a tener que crear un mock HttpServletRequest y un mock HttpServletResponse.
Consejo: Los mock objects son útiles, pero si tus tests necesitan muchos, probablemente le estés pifiando en la separación de responsabilidades.
Entonces separando responsabilidades termine con algo así:


Para los "Templates" no hubo mucho análisis de mi parte: implementar la generación de HTML dentro del servlet es engorroso e inmantenible (en este caso la necesidad de separar responsabilidades es bien clara). Para implementar los templates use los GStrings de Groovy. Me pareció bueno mantener las cosas bien concretas: el template por ahora se utiliza para visualizar una pagina.
Nota: En el código actual en GitHub van a ver que el uso de los templates evoluciono hacia algo más generalizado, en otros posts les cuento el por que.
¿Por que Writable?
Writable es una interfaz de Groovy que simplemente describe el método "writeTo(Writer)". Podría haber usado String, pero usar Writable permite expresar solo lo que necesito y optimizar las cosas si fuese necesario.
Si te estas preguntando a que me refiero con "optimizar": si uno tiene una pagina grande es preferible hacer un "streaming" que guardar toda la pagina en un gran String. Lamentablemente el parser de Markdown que estoy usando no permite hacerlo, pero como están planteadas las cosas podría usar otro parser sin afectar al resto de la aplicación.

¿Por que un objeto Page y no retornar directamente el String con el contenido?
Esta claro que para mi sistema una pagina no es simplemente un String.
Un fanático de TDD y del "paso a paso" me diría que para el test de mostrar la página un String alcanza. Sin embargo sé que voy a querer modelar más cosas de una pagina: una pagina tiene un titulo, una representación HTML y una representación en formato wiki.

¿Por qué una interface PageProvider?
Bueno yo tambien tengo la misma duda :)
Por ahora solo tengo una implementación de PageProvider y tampoco tengo intenciones de tener una distinta a futuro. En este punto hay dos cuestiones basadas en la experiencia que me llevaron a esto:
  • Una interfaz PageProvider me facilitaría la creación de mocks en el caso que quisiera testear otros componentes que dependan de un PageProvider (si esta es una de las "malas" costumbres adquiridas de la experiencia en Java).
  • Si quisiera agregar un cache podría usar un decorator que implemente esta interfaz.
    Otra vez me estoy adelantando -son las manias que uno adquiere de la experiencia previa- en estos casos es importante tomar nota mental de que uno se esta adelantando. A veces por adelantarse, uno le puede errar fiero (de hecho me paso con la búsqueda, pero eso se los voy a contar en otro post). En este caso decidí seguir adelante: le veo mas ventajas que contras, pero si les pasa algo asi en un diseño y les queda la duda... paso a paso como diría mostaza.

Espero no haberlos aburrido mucho, la próxima les cuento algunos pasos más.