13 julio 2009

Estados

Cuando encuentro código del siguiente estilo:

if (factura.getEstado() == EstadoFactura.PAGA) {
    // código
} else if (factura.getEstado() == EstadoFactura.ENTREGADA) {
    // código
} else {
    // otro
}

no puedo evitar pensar: "¡horrible!". Por que conozco los problemas que genera este tipo de código y sé que con otro diseño pueden evitarse.

La cuestión es explicar como mejorar el diseño. En estas situaciones, mis intentos de explicación son frases un tanto pedantes como: "este uso del 'if' es feo, por que las decisiones sobre que hacer según un 'estado', en objetos se pueden resolver utilizando polimorfismo".

Lamentablemente este esbozo de explicación no ayuda, y noto que en general produce la siguiente reacción: 
"Todo muy lindo esto de objetos, mensajes y polimorfismo. Pero tengo en mi tabla FACTURA un campo ESTADO y esto refleja directamente eso. Además ¿Cual es la solución? ¿Usar el patrón State? ¿Hacer un montón de clases para algo que resuelvo en un 'if'? Dejemos la 'estética de objetos' para la teoría." 

Una explicación técnica...

Voy a comenzar por el camino común de dejar en claro por que en objetos este tipo de "if" puede evitarse.

El siguiente ejemplo:

if (figura.getTipo() == Figura.RECTANGULO) {
    area = figura.getBase() * figura.getAltura();
} else if (figura.getTipo() == Figura.TRIANGULO) {
    area = figura.getBase() * figura.getAltura() / 2;
} else if (figura .... ya se hacen a la idea de como sigue

puede resolverse mediante clases que representen a los rectángulos, triángulos, etc. 
Si todas estas clases son polimorficas con el mensaje "getArea()" entonces toda la seguidilla de "if" puede resolverse en una sola línea: figura.getArea().
Donde el receptor del mensaje es quien encapsula la decisión de como hacer las cosas.


Complicando el problema

Muchas veces es deseable que el algoritmo a ejecutar según el tipo de objeto este separado de los objetos.
Por ejemplo si Figura posee un método "dibujar" es probable que el algoritmo de dibujo genere dependencias con un framework de dibujo, y quizás no quiero "atar" al modulo de figuras con un framework de dibujo en particular, o quizás el algoritmo de dibujo varíe según el contexto.

Para este tipo de casos las alternativas son usar Double Dispatch o bien algún tipo de interfaz entre distintos frameworks/implementaciones donde en este caso dibujar seria algo así como "dibujarSobre(unCanvas)" siendo "unCanvas" es un objeto que implementa esa interfaz común.

Pero volviendo al problema original (de la Factura y Estado), hay algunas cuestiones a tener en cuenta:
  1. En el caso de la factura el "if" se hace en base al estado y no a un "tipo de factura", eso significa que a diferencia del ejemplo de las figuras el estado puede variar una vez creada la factura.
  2. Crear un objeto que represente el estado y usar Double Dispatch no parece ser en este caso una buena solución. (El por que lo dejo como ejercicio)

¿Cuál es la solución a este problema?
Rta: Examinar mejor el dominio:

  • ¿Tiene sentido hablar de "estado" de una factura?
  • ¿Que representa la factura?
  • ¿Realmente el cambio de "estado" representa el cambio de estado de una misma cosa o en realidad representa cosas distintas?

Examinando el dominio

NOTA: El ejemplo de Factura/Estado es ficticio, pero creo que captura muchos de los casos de negocio donde encontré código del estilo "if estado then ...".

En "Stan's Shop" la gente agrega productos a su pedido, paga en la caja donde se le entrega una factura, finalmente en el mostrador de entregas un empleado prepara los productos y una vez que se los dio al cliente coloca un lindo sello de "ENTREGADO" en la factura (un esquema similar al que siguen todos los locales de comida rápida del micro-centro).

Los diseñadores del sistema pensaron que era bueno tener un objeto "factura" especificado por la siguiente clase:


Este diseño tiene varios problemas:
  • Si la factura se ve como un comprobante de pago (al momento de pensar este ejemplo ficticio desconozco si en el negocio tiene otros usos), entonces la factura debe ser inmutable. Este hecho se refleja en que agregarItem y quitarItem generan error: solución problemática por que en todos los lugares donde quiera enviar estos mensajes tengo que tener en cuenta la posibilidad de error. Lo mismo ocurre con getNumero donde se debe chequear por null.
  • La "mutabilidad" de factura genera otros problemas de implementación, pero prefiero dejar los detalles para otro post sobre "side effects".
  • ¿Cómo sé cuando una factura es "valida"? Puedo chequear por getEstado() == PAGA o ENTREGADA, puedo chequear por getNumero() != null o agregar un nuevo método esValida (de intention revealing ni hablar). En la práctica encontré que se mezclan las tres formas dependiendo del programador, lo que genera algunos dolores de cabeza al momento de hacer un refactoring.
  • ¿Tiene sentido tener un estado "ENTREGADA"? ¿El hecho de saber si los productos fueron entregados o no, no es responsabilidad de otra área de negocio? Este es un ejemplo ficticio y no aporta mucho escarbar en detalles de negocio, simplemente lo menciono por que en la mayoría de los casos los problemas de diseño muestran una falla en reconocer objetos del dominio.

La solución a este problema es sencilla:
  • La factura representa para mi un comprobante de pago y nada más. Por lo tanto una vez generada no puede modificarse ya que tiene implicaciones contables (y quizás legales).
  • Cuando el cliente llega al local y elige los productos que quiere no esta trabajando sobre una "factura", si no sobre una especie de "carrito de compras".
  • Saber si se entregaron o no los productos no es responsabilidad de la factura, si no del sistema que lleva en control de las entregas.



Con este diseño la separación de responsabilidades es clara, no hay forma que en el código modifique items en una factura, o de pedirle el número de factura a un CarritoDeCompras, por lo tanto no hay necesidad de verificar un "estado".
El caso de "ENTREGADA" lo voy a discutir a continuación por que me sirve para ejemplificar otro "patrón" común.

Cambios de estado sin cambios de comportamiento

En el ejemplo el cambio de CarritoDeCompras a Factura implica un cambio en la semantica de los objetos. ¿Pero que pasa con el sello de "ENTREGADA"? ¿No se puede pensar como un simple flag booleano en una Factura?

No quiero entrar en detalles de este dominio ficticio, pero quizás un "flag" no alcance. Es probable que quiera registrar quien realizo la entrega, a que hora, etc.
Por eso voy a hacer una simplificación: no me interesan esos detalles, solo quiero el equivalente al sello de "ENTREGADA".

Y dado que en este ejemplo yo pongo las reglas, voy a suponer que el operador en el mostrador de entregas tiene una pantalla que muestra las facturas pendientes de entrega, donde con un click puede cambiar el estado a "ENTREGADA".

Una forma de diseñar esto es pensar que uno tiene un "sistema" (modulo, o como deseen nombrar) que lleva el control de los pedidos, con dos "recipientes": uno para la facturas pedientes y otro para las entregadas. Entonces pasar de estado es mover la Factura de un recipiente a otro:





¿Pero el booleano no era mejor? No, este esquema tiene muchas ventajas más:
  • Factura sigue siendo inmutable, e independiente de como llevo el control de entregas.
  • SistemaDePedidos puede variar fácilmente, llevando por ejemplo el control de la fecha de entrega, etc.

¿Y la UI? "Quisiera hacer una página web que muestre el listado de facturas entregadas y no entregadas, con getEstado() o el flag es mucho más fácil".

En este caso también es fácil por que puede resolverse de la siguiente manera:
  1. La UI puede pedir al SistemaDePedidos directamente las Facturas entregadas o las no entregadas.
  2. Supongamos que la solución 1. no es satisfactoria: por como se utiliza la UI realmente se quiere tener un objeto al cual se le pueda pedir el estado. En ese caso conviene hacer un objeto especifico para la UI, este objeto puede verse como un DTO que se usa solo para transferir datos a la capa de UI.

¿Y la base de datos?
"Tengo una tabla Factura que tiene la columna ENTREGADA"

No hay problema: las herramientas de mapeo O/R permiten hacer mapeos complejos de asociaciones para resolver el caso entregada/pendiente en el SistemaDePedidos, o si es necesario discriminar la subclase a mapear.

Conclusiones

En el ejemplo ficticio que di en este post aplica a muchos dominios, algunos ejemplos que me vienen a la mente son: estados de un documento (publicado/no publicado) en un sistema de CMS, registración de compras, distinción entre un estado de "edición/en uso" para una configuración (ejercicio: examinen el código fuente de Struts y vean como la implementación de configuración puede refactorizarse y mejorarse usando lo que comente en este articulo), etc.

Creo que en la balanza de "bueno/malo" el código del estilo "if estado then codigo" tiene muchos problemas:
  • Probablemente no modela adecuadamente el negocio.
  • El "contrato" de los objetos es complicado: todo el tiempo dependo del estado para saber si puedo o no usar ciertos métodos.
  • En consecuencia el código se empieza a "contaminar" con este tipo de chequeos, con el agravante que quizás cada programador lo haga de una forma distinta según la situación, dificultando el mantenimiento y refactoring.
  • Se necesitan objetos mutables, en casos donde no conviene que lo sean (prometo hablar sobre "side effects" en otro post).

Mientras que las únicas ventajas que le veo son que es simple de implementar para ejemplos chicos y fácil de mapear a una tabla en la base de datos.

Por todas estas razones conviene evitar este tipo de "ifs" y en lo posible evitar el uso de Enums ya que fomentan este tipo de código.


09 julio 2009

Presentaciones

Empecé este post hace más de un mes: ¡que mes!
Estuve al limite del stress total, despierto hasta tarde para llegar a las entregas del trabajo y la facultad.
Por eso, este pequeño descanso (producto de los cierres por Gripe A) me viene muy bien.

Advierto: va a ser un post largo con recomendaciones sobre como crear presentaciones.

Estas recomendaciones provienen de:


(más las diferentes experiencias que tuve dando clases y presentaciones)

Ahi vamos...

Presentar != Dar una clase

Es común que se confundan ambas cosas.

Las presentaciones tienen una forma de comunicación donde se trata de dejar un mensaje en poco tiempo (en cierta forma tratan de "vender" una idea/tecnología/método/etc.).
En cambio una buena clase (para mi) debería fomentar la formación de "teorías", quizas mediante la experimentación o el debate (se nota que últimamente estuve leyendo a Freire).

En otras palabras: un curso no es solo mostrar slides y hablar frente a los alumnos, hay además una cuota de participación y experimentación que es fundamental para aprender. Este post trata sobre presentaciones, pero es bueno mencionar la diferencia, en mi experiencia la mayoría de la gente piensa que preparar unos cuantos slides es lo mismo que preparar un curso.

Olvidarse del PowerPoint y focalizarse en el mensaje

Buscando información sobre como hacer presentaciones me encontré con tips inútiles que se centran en cuestiones cosméticas de dudosa efectividad.


Lo importante es transmitir un mensaje. Si el mensaje es bueno y esta bien transmitido la gente va a recordar la presentación (Tudor Girba lo cuenta de una forma excelente).

Es fácil caer en la tentación de querer transmitir demasiado. Por ejemplo, hace poco di una presentación sobre usabilidad en el Microsoft Architecture Day (MAD): tenia ganas de contar muchas cosas y no aguante la tentación de querer compactarlas en 45min. El resultado: partes aburridas y por los comentarios que tuve me di cuenta que no todos captaron lo que quería transmitir.

Como suelo trabajar: Antes de armar una presentación, me dedico a investigar y leer la información que tengo. Con esa información voy armando un MindMap (en software o a veces en un cuaderno). De ahí voy sacando en limpio el mensaje que me gustaría transmitir (esa es la tarea más difícil).

Transmitir el mensaje contando una historia

La charla esta por empezar. El proyector y la maquina están encendidos, pero misteriosamente dejaron de funcionar, más de 20 personas están esperando y la única salida es usar el pizarrón. ¡Que no cunda el pánico!: tenemos el mensaje a transmitir, solo nos queda contarlo.

¿Se puede contar una historia en base a un tema técnico y especifico? ¡Si! un claro ejemplo son los documentales. Tomen cualquier documental de 2hs e imagenlo en PPTs de bullets y voz en off. ¡Un embole! se quedan dormidos a los 10min, y si están dormidos dificilmente puedan seguir la presentación.

Una vez que se tiene el mensaje lo mejor es ir pensando en como contarlo  sin PowerPoint. Mientras piensan como contar las cosas es probable que imaginen diagramas y graficos que ayuden a contarlas, esas ayudas son los graficos que van a ir en los slides (el "texto" es lo que uno cuenta con palabras, no es necesario repetirlo en bullets).

Como suelo trabajar:
Decidido el mensaje, y trato de redactar en un cuaderno lo que voy a ir contando. Aunque esta redacción quede en el cuaderno es muy útil, por que me sirve para definir las cosas que voy a visualizar con la computadora.

Contar la historia usando visualizaciones

La importancia de la computadora es proveer visualizaciones que sirvan como soporte a lo que estamos transmitiendo.

Los bullets no sirven como visualización: hay un problema con la transmisión de texto escrito y voz al mismo tiempo. Este problema se conoce como sobrecarga cognitiva y se debe a que nuestro cerebro procesa el lenguaje hablado y el texto de la misma forma. Para comprobarlo pueden hacer un experimento: inserten una película en DVD que tenga audio en español y subtítulos en español, activen ambos y traten de ver la película, al rato van a filtrar alguna de las dos cosas.
Leen el texto o escuchan el audio, hacer las dos cosas es muy molesto.

Cuando se muestra una imagen/diagrama como soporte ocurre otra cosa: la visualización ayuda a explicar el tema (por eso también debería ser conceptualmente buena).

Como suelo trabajar:
No uso bullets, la información "textual" trato de decirla con palabras y usar el proyector como complemento visual. Esta parte es muy trabajosa por que a veces hay que armar las imagenes, o buscar las fotos adecuadas (Cooliris es muy útil en estos casos).
Trato que los slides sean simples, y cuando armo figuras me sirve mucho imaginar como las voy a explicar.
También me resulta útil pensar en la presentación como si fuese un documental, donde soy la voz en off y junto con las imagenes voy contando la historia.

Algunos consejos para diseñar y crear visualizaciones

En las presentaciones suelo recurrir a dos tipos de ayudas visuales:
  1. Para reforzar ideas/mensajes.
  2. Para explicar mediante diagramas esquemáticos.

Para crearlas es necesario tomar el rol de "diseñador grafico" y dado que probablemente no somos diseñadores profesionales, es necesario recurrir a algunos tips.

Para el primer caso suelo usar fotos y texto (una frase o titulo corto). Lo importante es mantener bajo el "ruido" visual, especialmente en las fotos: una foto con muchos "focos" de atención (sujetos, colores, texturas) suele distraer.

El segundo caso es más complejo. Primero por que la resolución del proyector es limitada y a veces es necesario partir un diagrama esquemático en más de un slide (lo cual rompe un poco el flujo de la presentación). En este caso la palabra clave es: simplicidad. Simplicidad de diseño y de conceptos: para transmitir un concepto visualmente es necesario encontrar las analogías adecuadas (Envisioning Information de Tufte es una fuente de información muy buena sobre como crear visualizaciones), evitando cosas superfluas como clip-art o efectos visuales.

Resumiendo:
  • Simplicidad (evitar el ruido):
  • No es necesario poner en logo de la empresa en cada slide: nos saca lugar para los diagramas y nadie los ve, solo generan ruido.
  • Lo mismo aplica para los "bordes" usados en muchos templates de PowerPoint.
  • Si el slide queda muy complejo: pensar en partirlo en varios. (las presentaciones de Microsoft son un excelente ejemplo de como generar ruido visual inútil).
  • Ser consistente con las fuentes y colores. Creo que esta es la mejor forma de dar una identidad de empresa. Las presentaciones de Apple son un buen ejemplo, hagan este experimento: creen una presentación usando el font Myriad Pro, con letras blancas sobre un gradiente azul oscuro, despues preguntenle a algun "techie" a que les hace acordar la presentación (apuesto que más de uno va a decir Apple).

Otros tips

Control remoto: Es impresionante como ayuda tener uno, dan mucha libertad para hablar.
Suelo usar el que vino con mi notebook, pero tambien hay un mouse bluetooth de Microsoft con botones para controlar la presentación (algunos proyectores tienen un cable USB que permite manejar la presentación por control remoto, lamentablemente en la mayoría de las salas el proyector esta en el techo y sin acceso al cable).

Transiciones: No siempre son malas, lo que si es cierto es que la transiciones de PowerPoint o OpenOffice son tan feas que es mejor no usarlas.
Las de Keynote son otra historia, sobre todo Magic Move que me encanta, suelo utilizarla para no perder el flujo de la narración. Imagino como un documental, es decir no freno la presentación para hacer la transición de slide a slide, sino que lo presento en continuo (esta excelente presentación en TED usa este estilo), para este caso es muy importante contar con un "control remoto".

Para las transiciones aplica el mismo consejo que para las visualizaciones: muchos efectos locos generan ruido que no aporta nada.
 

Grabación: Nunca había grabado las presentaciones que di, hasta que para el Microsoft Architecture Day tuve que hacerlo (era un pedido de los organizadores), y encontré que es muy importante para aprender y mejorar.

Mientras veia la presentación vi muchas cosas a mejorar en el contenido y en mi forma de hablar.


Si dan la presentación con una notebook no necesitan nada para grabar (me refiero a grabar el audio junto con lo que muestran en pantalla): la mayoría trae microfono incorporado.

En Windows: Pueden instalar Camstasia para grabar la pantalla y el audio (incluso trae un plug-in que se integra con PowerPoint).
En Mac: Keynote ya trae una opción para grabar y exportar el video.

Bueno eso es todo :), les deseo un buen finde bien lejos de "la porcina".