lunes, 31 de agosto de 2009

Empresa de software promete resolver el problema de las emisiones de CO2, la tala del amazonas, el uso indiscriminado de papel…

Bah, más o menos. En realidad la noticia dice

Empresa de software promete plantar un arbol por cada bug que tengan

Visto en Menéame.

Día del Blog 2009

blogday_140x280_2009

Un año más, otro día del blog (como si hubiese participado en tantos, este es apenas mi segundo). Aquí van mis recomendaciones (sin un orden en particular):

Yoriento: Yoriento es, para utilizar su propia definición, un blog sobre “orientación profesional, búsqueda de trabajo, empleo 2.0, productividad personal, coaching, psicología en la empresa, networking, recursos humanos en internet”… y se queda corto. Difícil encontrar un tema que Alfonso Alcántara no haya tocado con seriedad pero a la vez con un estilo desenfadado y repleto de buen humor. Alfonso es (muy) adicto a su twitter, y vale la pena seguirlo.

Navegápolis: un blog de lo que me gusta, desarrollo de software, sin más. Mucho Scrum, agilidad, recursos, opinión sobre herramientas, métodos, arquitecturas, patrones, situaciones laborales, mucho humor y trazo grueso, sin bajar al detalle (a veces exasperante) del código puro.

lboisset’s Ruminations: “Ibo” anda un poco disperso últimamente, quejándose de excesos de trabajo y experimentando con su posterous (ay, estos chiches nuevos que me cuesta entender). Sincero y personalísimo como pocos, es el blog del jefe de proyectos que todos quisiéramos tener.

Pons Asinorum: es raro Pons, de nariz respingada y aires un tanto aristocráticos (“El hablar, por su facilidad, puede ser imitado por todo un pueblo; la imitación en el pensar, del inventar, ya es otra cosa.”). Pero atrae. A veces no puedo estar más de acuerdo, a veces sus ideas me resultan brutalmente desagradables, pero siempre interesantes y de irreprochable argumentación. Desafiante.

La hora de la pavada, 3x1 para lo último. No todo es seriedad en la vida, no todo es trabajo, no todo es pensar. Existe el cine shampoo, y existe el blog shampoo, aquellos en los que entramos y de los que no podemos salir, que siempre nos sacan una sonrisa, que nos tienen un par de horitas a la semana pensando en nada: There, I fixed it, Fail Blog, y el increíble Awkward Family Photos.

Seguiría recomendando, pero bueno, hay que ser breve para que sirva. El resto está por aquí, en el blogroll (en la barra de la derecha), referenciados en las entradas, compartidos en twitter, friendfeed o delicious.

Relacionada: mis recomendaciones del año pasado.

domingo, 30 de agosto de 2009

20 sitios web muy mal diseñados.

La gente de Manolith ha recopilado estos sitios en su entrada 20 of the Worst Designed Websites In the World.

Si bien hay algunos que -en mi opinión- no acumulan suficiente mérito como para ser incluidos en semejante “lista de la vergüenza”, el resultado final del recorrido es un fuerte mareo y dolor de cabeza.

Lo malo no es ser incapaz de crear un buen diseño –yo soy incapaz de ello- sino el mal gusto liso y llano –y la falta de un poco de sentido común- que impide al creador del sitio (la frase “diseñador del sitio” está fuera de lugar, queda claro que no todo el que diseña es diseñador) reconocer que algo no está del todo bien.

Un par de ejemplos:

014

094

143

A ver si alguno de los incluidos en esta lista se decide a pedir ayuda profesional, o por lo menos a robar inspirarse en algún sitio un poco más sobrio y bien diseñado.

Encontrarán la lista completa y los links a los sitios (si es que se quedaron con ganas) en el artículo original.

Visto en Menéame.

sábado, 29 de agosto de 2009

500+ entradas.

500 posts (cuac). Hace apenas un par de minutos recordé que estaba cerca de las quinientas entradas, hito que merece algún tipo de comentario (aunque sea para rellenar un sábado de poca inspiración) o algún post especial, de acuerdo con la humana fascinación por los números “redondos”.

Bueno, el hecho es que… se me ha pasado. Ésta es la entrada número 504 desde aquélla que dio el puntapié inicial, el 17 de mayo del año pasado.

Irónica y casualmente la entrada 500 ha sido Frases: pereza y apatía.

Bueno, eso es. Gracias a todos por sus visitas y comentarios, es ese feedback lo que me da la motivación para seguir. Nos leemos.

viernes, 28 de agosto de 2009

Jueguitos de Viernes: Sin Mark.

Mezcla de juego de rol, aventuras y puntería, Sin Mark recuerda un poco al Bowmaster Prelude en el modo de juego.

Aquí representamos a un mago/arquero (la verdad que no leí la historia), y para pasar de nivel debemos destruir los portales de donde salen toda una serie de enemigos, al tiempo que recogemos runas con las que podremos crear hechizos al final de cada etapa.

Sin Mark

Muy dinámico, con buenos gráficos. Vale tanto para cortar 10 minutos o pasarse toda la tarde.

jueves, 27 de agosto de 2009

Los mejores chistes gráficos de programación (o de programadores… o de cualquier cosa que parezca geek).

Más hilos “interesantes” en StackOverflow (como éste, éste y éste otro): “What’s your favorite “programmer” cartoon?

Dilbert y XKCD se llevan la mayoría de las menciones (yo pondría más arriba la de Geek Hero Comic o incluiría alguna Sinergia Sin Control si hubiese lugar para alguna mención en español), pero hay algunos menos conocidos por descubrir e incluso un par de muy buenas ilustraciones anónimas de esas que circulan por ahí:

Simplicity. Este es tan cotidiano que me dan ganas de llorar (creo que ya lo había mencionado por aquí).

simplicity

Born brave, otra versión de un viejo conocido.

DevelopersAreBornBrave_Small

The evolution of the sigh…

phd120804s

Programmer (estoy peligrosamente cerca de la 8…)

programmer

En fin… a dormir. El resto aquí.

miércoles, 26 de agosto de 2009

Los problemas de XML en el manejo de grandes cantidades de datos.

No es el uso, es el abuso, que puede transformarse en delirio.

Mi opinión sobre el uso (resumen de los links anteriores) está muy bien condensada en uno de los puntos del artículo: XML es perfecto para para crear documentos estructurados que puedan ser manipulados “como cajas negras” por aplicaciones o, más apropiado todavía, interpretados fácilmente por seres humanos. Por ejemplo:

tatuaje-disenador-web xmlmodelo_2

…pero si tenemos que compartir una gran cantidad de datos entre aplicaciones con una estructura preestablecida… ¿Para qué especificarla junto a los datos mismos si está, justamente, preestablecida?

Tal el caso que se ilustra en how xml threatens big data, un artículo reciente de Dataspora Blog (en inglés) donde el autor comenta brevemente su fracasado intento (enderezado a tiempo) de usar XML como formato para compartir grandes cantidades (ahí el problema) de datos entre aplicaciones.

En resumen, tres razones para el fracaso:

  1. XML aumenta la burocracia: creación, parseo, tipado, conversión…

  2. El tamaño importa. XML encaja bien para documentos (cantidades de información manejable por seres humanos), pero no para datos (cantidades de información manejable por aplicaciones).

  3. La complejidad tiene su costo. XML es mucho más complejo que otros formatos (CSV, JSON).

… y una propuesta de tres reglas para “Rebeldes del XML”:

  1. ¡Basta de inventar nuevos formatos XML!

  2. Obedece la regla de los 50 15 minutos: no debería llevar más de 50 15 (¡ay! mi inglés) minutos aprender el formato.

  3. Adoptar modelado de datos tardío (Lazy Data Modeling), un concepto comparable a la “evaluación tardía”: grabar los datos como son y dejar su interpretación (tipado) para el momento en el que se los requiera, implementando esa interpretación de acuerdo a las necesidades del caso. Es un un punto que tal vez merece más reflexión y que da para mayor discusión.

Ése es el resumen, mucho más detalle por aquí, en el artículo original.

martes, 25 de agosto de 2009

Frases: pereza y apatía.

No confundir la pereza con la apatía. Los apáticos no se interesan por nada; los perezosos nos interesamos pero no hacemos nada.

Twitteado por @Yoriento
(me dio pereza verificar si es de su autoría, asumamos que sí).

lunes, 24 de agosto de 2009

Cuando los resultados no reflejan la experiencia o el aprendizaje.

Hay veces que llama poderosamente la atención la brecha (otras veces el abismo) entre la calidad de un equipo y la del proyecto sobre el que trabaja. A veces para bien, a veces para mal.

StaffEn algunos se percibe una capacidad para la mejora que excede en mucho la capacidad técnica (inicial) de sus integrantes. La falta de experiencia hace que se reinventen muchas ruedas y se cometan muchos errores de esos que (tal vez pecando de un poco de soberbia) los desarrolladores de más experiencia catalogan de “típicos”, pero que son ampliamente compensados por la motivación a corregirlos, incluso en varias aproximaciones, intentando una y otra vez. Éste es el caso ideal en el que, en todo momento, la calidad del proyecto refleja todo el aprendizaje y la experticia del equipo.

Pero en otros se ve que el resultado, si bien funcional, no tiene la calidad que podría esperarse de acuerdo la experiencia de los participantes, o que surgen (en el peor de los momentos) problemas que derivan de errores que podrían haber sido detectados (e incluso corregidos) con relativa facilidad por los miembros de más experiencia, problemas que luego de un tiempo de incubación terminan impactando transversalmente en todo el proyecto volviéndose de difícil o imposible solución.

Mucho tiene que ver en la calidad de un proyecto la habilidad para resolver problemas de sus integrantes. Mucho más tendrá que ver la experiencia y muchísimo más la motivación.

Es la experiencia la que permite ganar tiempo aplicando soluciones ya probadas y enfocarse en aplicar la habilidad en resolver aquellos problemas que hacen único a cada producto de software. Pero es la motivación lo que lleva a utilizar la (mucha o poca) habilidad disponible y aprender (con mayor o menor velocidad) de los errores y aciertos (propios y ajenos), que es lo mismo que decir “adquirir experiencia”.

Pero una cosa es la motivación a aprender y otra la de aplicar el resultado de ese aprendizaje al proyecto en particular sobre el que se está trabajando y aprendiendo, y de ahí la brecha entre capacidades y realidades que mencionaba al principio. La mayoría aprende de los errores y ve posibilidades de mejora, pero son menos (muchos menos) los que desandan el camino para corregirlos o implementar esas mejoras en el código ya escrito. ¿Por qué?

Se me ocurren un par de factores que hacen de esa decisión (la de seguir sin mirar atrás) la más razonable:

  • Si el alcance del proyecto es acotado y está próximo a entrar en una etapa de mantenimiento (¡exclusivamente!) correctivo, de poco o nada servirá mejorar lo que de todas maneras funciona (suponiendo que funciona razonablemente, ya que de no ser así no hay alternativa posible).

  • Si el problema es transversal y está demasiado extendido, puede ser mejor será tratar de no cometerlo de aquí en más y corregirlo sólo en donde las modificaciones sean imprescindibles.

  • En cualquier caso en el que el impacto sea menor que el esfuerzo que requiera la corrección. El problema aquí es la medición de ese impacto, para la que deberíamos considerar no sólo la situación actual sino también a futuro. Si estamos desarrollando un producto que se pretende mejorar y ampliar indefinidamente (en vez de “cerrar y entregar”) la acumulación de pequeños problemas terminarán afectando la calidad haciendo cada vez más difícil implementar nuevas funcionalidades y forzando una (mucho más riesgosa y costosa) reingeniería.

En definitiva, es cuestión de decidir en forma consciente y explícita si vale la pena el esfuerzo dada cada situación en particular. Pero hay otros factores, menos técnicos y más humanos o de organización, que bloquean las alternativas:

  • Simple resistencia al cambio, el aferrarse a un estado en el que los problemas (graves o no) son conocidos.

  • Una patológica actitud defensiva puede hacer que cualquier sugerencia se vea como una crítica.

  • Soberbia, que impide ver los problemas y desventajas que inevitablemente están presentes en cualquier solución.

  • Desinterés o el clásico “sólo hago lo que me ordenan”.

  • Escaso trabajo en equipo o excesivamente compartimentado, que hace que se pasen por alto los problemas y posibles mejoras transversales.

Resistencia al cambioSon estos factores son los que hay que detectar y combatir porque son los que llevan a decisiones irracionales o por defecto (aquellas que surgen de la no-decisión). El ideal es que el equipo tenga el control del proyecto, lo que implica ser consciente de los aciertos, de las cuestiones “mejorables”, de los errores que hay que corregir y de los que hay que soportar.

domingo, 23 de agosto de 2009

100 años de efectos especiales.

Un video que condensa en cinco minutos los avances en efectos especiales de los últimos cien años con una buena cortina musical (Rods and Cones, de Blue Man Group). El resultado es impresionante.

Las películas referenciadas son:

  • 1900 – The Enchanted Drawing
  • 1903 – The Great Train Robbery
  • 1923 – Los 10 Mandamientos (Cine mudo)
  • 1927 – Sunrise
  • 1933 – King Kong
  • 1939 – El Mago de Oz
  • 1940 – El ladrón de Bagdad
  • 1954 – 20.000 Leguas de viaje submarino
  • 1956 – Forbidden Planet
  • 1963 – Jason y los Argonautas
  • 1964 – Mary Poppins
  • 1977 – Star Wars
  • 1982 – Tron
  • 1985 – Volver al Futuro
  • 1988 – Quien engañó a Roger Rabbit
  • 1989 – Abismo
  • 1991 – Terminator 2: Día del Juicio
  • 1992 – The Young Indiana Jones Chronicles
  • 1993 – Jurassic Park
  • 2004 – Spider-Man 2
  • 2005 – King Kong
  • 2006 – Piratas del Caribe: Dead Man’s Chest
  • 2007 – Piratas del Caribe: At World’s End
  • 2007 – La Brújula Dorada
  • 2008 – The Spiderwick Chronicles
  • 2008 – El Curioso Caso de Benjamin Button

Visto en Aeromental.

viernes, 21 de agosto de 2009

Jueguitos de Viernes: Mad Shark.

El único objetivo en Mad Shark es mantenerse con vida la mayor cantidad de tiempo posible, escapando de los buzos (somos el tiburón, por si no lo notaron).

Hay que balancear bien la velocidad con el consumo de energía, que podemos recuperar comiendo todo lo que se cruce en nuestro camino… básicamente peces y buzos (y medios buzos), así que se pone un poco sangriento.

Personalidades sensibles, abstenerse.

MadShark

jueves, 20 de agosto de 2009

Frases: del arte y la herramienta.

La informática tiene que ver con los ordenadores lo mismo que la astronomía con los telescopios

Edsger Dijkstra.

(twitteado por @Yoriento)

miércoles, 19 de agosto de 2009

10 Principios para el diseño de la interfaz del usuario.

drowsy_2_080228_ms

“A modern paradox is that it’s simpler to create complex interfaces because it’s so complex to simplify them.”

– Pär Almqvist

  1. Conoce a tus usuarios.
  2. Presta atención a los patrones.
  3. Mantente consistente.
  4. Jerarquiza visualmente.
  5. Da feedback en cada acción.
  6. Soporta los errores del usuario.
  7. Da el control al usuario.
  8. Habla su lenguaje.
  9. Mantenlo simple.
  10. Avanza, actualiza, no te estanques.

Desarrollados en el artículo original (en inglés): 10 User Interface Design Fundamentals (vía @DeliciousHot).

martes, 18 de agosto de 2009

Trabajo remoto.

Las herramientas de comunicación han avanzado muchísimo, qué duda cabe. Es posible sentarse en el escritorio de la oficina desde casa de una forma razonablemente transparente utilizando una VPN, conversar con los demás miembros del equipo por chat, por voz o video a través de servicios que prácticamente todo el mundo utiliza de forma muy natural y cotidiana. Si pensamos en cómo este conjunto de tecnologías –de comunicación- ha impactado en la economía y las relaciones laborales veremos gran dificultad en imaginar en qué las transformará de aquí a 20 o 30 años.

En el rubro del desarrollo de software -integrado necesariamente por personas y empresas acostumbradas y abiertas a la tecnología- todos, en mayor o menor medida, utilizamos algunas herramientas de trabajo remoto. Aquellos con más experiencia recordarán con extrañeza los no tan lejanos tiempos en los que instalar, configurar, verificar un problema o arreglar una emergencia, eran todas tareas que implicaban moverse e ir a visitar al cliente.

De un tiempo a esta parte -apenas un par de años-, la “presencia virtual” se está extendiendo hacia dentro del equipo de desarrollo, al que siempre imaginamos como un montón de personas trabajando juntas en el mismo lugar. Si bien la imagen anterior sigue representando la norma, opciones de trabajo remoto comienzan a analizarse seriamente como una posibilidad por cada vez más organizaciones, y comienzan a configurarse otro tipo de equipos, a los que usualmente se nombra como equipos distribuidos.

video-confPor otro lado, desde lo ágil siempre se ha dicho –y yo comparto- que la coordinación y comunicación es a la vez demasiado importante y demasiado compleja en el desarrollo de un producto de software, por lo que deberíamos optar, siempre que sea posible, por la forma más eficaz de coordinar y comunicar: cara a cara.

Debemos ser conscientes de que al utilizar estas nuevas tecnologías estamos, en mayor o menor medida, sub-optimizando la comunicación. Es una opción racional si -y sólo si- los beneficios superan el costo de esa sub-optimización. Es decir que si para una consultora argentina tener un cliente español no representa ninguna dificultad operativa seria, no por ello tiene sentido hacer una videoconferencia con otro cliente cuyas oficinas están a escasos 200 metros. Una software factory que alquile oficinas con espacio e infraestructura suficiente para todos sus empleados no se beneficiará de las posibilidades del trabajo remoto en tanto no reduzca el costo del alquiler o capitalice una mayor velocidad de respuesta como nuevos clientes o precios más altos en cada contrato, o en un aumento medible de la productividad.

simpsons-homer-working-from-homeYendo un poco a lo personal, para los miembros de un equipo tener la posibilidad de trabajar remotamente implica libertad de horarios y –sobre todo- el aprovechamiento de esos momentos de inspiración o entusiasmo, esquivando la saturación. Si a uno se le ocurre una gran idea un domingo a la mañana y tiene ganas y tiempo de probarla lo antes posible, ¿por qué no? Si se nos hace cuesta arriba un lunes a la mañana o un viernes a la tarde y las fechas límite lo permiten, ¿no sería más productivo entrar tarde o irse temprano, descansar, distraerse, en vez de luchar por terminar algo que en otro momento de mayor motivación nos llevaría la mitad de tiempo?

Suena bien, pero tiene un límite. Recordemos los costos de los que hablábamos al principio: de poco servirá todo ese trabajo adelantado si la funcionalidad había cambiado (la cambió el analista trabajando tarde a la noche desde su casa) y el desarrollador no se enteró por no chequear el correo.

Sobrepasamos un límite cuando, más seguido que esporádicamente, unos quedan a la espera de poder comunicarse con otros y los horarios no coinciden y comienzan a aumentar los tiempos muertos. Desde lo personal, ese costo de la libertad puede pagarse en forma de constantes llamadas y comunicaciones disruptivas por temas laborales que impiden una desconexión completa.

Para complicar las cosas las personas son extremadamente diferentes en este sentido. Algunas preferirían trabajar todo el tiempo en forma remota y sin horarios y otros ir a la oficina con un horario fijo, salir y no ser molestados para nada hasta el día siguiente. ¿Entonces? Dependerá de cada equipo encontrar el punto justo en el que todos se sientan cómodos, lejos de los extremos del control estricto o de la amalgama entre vida personal y laboral.

Personalmente creo que las herramientas de trabajo virtual deberían estar disponibles para todos, sobre todo teniendo en cuenta que en general las empresas ya cuentan con el software necesario (o hay opciones gratuitas o de muy bajo costo) y que no deberían demandar mucha infraestructura adicional a la que una empresa suele tener sólo por estar en el rubro de sistemas. Pero que al mismo tiempo deben establecerse ciertas reglas. Un horario en el que todos deberían estar en el mismo lugar, ciertas reuniones a las que sea obligatorio asistir en persona, cierta flexibilidad diaria (2 o 3 horas en una jornada de 8), una cuota de horas a cumplir por mes, ese tipo de cosas.

Debemos, en definitiva, tratar estas herramientas como a todas las demás: por más casos de éxito que escuchemos, por más buzz que se haya generado alrededor de ella, tiene que ser adecuada para nosotros.

lunes, 17 de agosto de 2009

Lápices vs. mouses.

La vida transcurre apaciblemente en la tierra del papel hasta que la llegada de la era digital desata una implacable batalla entre nuevas y viejas tecnologías.

Vi este muy buen cortometraje referenciado por cgr v2.0. En su post podrán encontrar algunos vínculos de interés, incluyendo entrevistas a los creadores.

domingo, 16 de agosto de 2009

Las mejores bromas y chistes de programadores.

krusty-el-payaso A alguien se le ocurrió preguntar "What is your best programmer joke?" en StackOverflow y el resultado es una de las mejores listas de bromas y chistes de programadores que vi. Aquí va mi top-5 (las traducciones son mías, más conceptuales que literales):

5) It´s not a bug, is a feature

Sin palabras…

featureiu1

4) Sql Join

An SQL query goes into a bar, walks up to two tables and asks, "Can I join you?"

3) Java y el sexo anal

Decir que Java es bueno porque funciona en cualquier sistema operativo es como decir que el sexo anal es bueno porque funciona en cualquier género.

2) PM perdido

Un hombre se pierde mientras pasea en un globo aerostático, así que desciende a pedir indicaciones y se da la siguiente conversación:

“Disculpe, señor, ¿puede decirme dónde estoy?”, pregunta al primero que cruza su camino.

“Usted está en un globo aerostático suspendido a 10 metros sobre este campo”, responde el transeúnte.

“Usted debe ser programador”.

“Efectivamente, ¿cómo es que sabe eso?”

“Porque su respuesta es técnicamente correcta, pero completamente inútil”.

“Y usted debe trabajar en management”.

“Es cierto, ¿cómo lo supo?”.

“Bueno, usted no sabe dónde está o a dónde se dirige, pero espera que yo sea capaz de ayudarlo. Finalmente, se encuentra en la misma situación que antes de encontrarnos, pero ahora es mi culpa”.

1) Confianza en el equipo

Un grupo de diez programadores es enviado a una clase para aspirantes a managers. El profesor comienza con esta pregunta:

“Ustedes trabajan para una empresa que desarrolla software de control de vuelo para aviones. Durante un viaje de negocios, justo antes de un despegue, notan una placa que indica que el avión está utilizando una versión beta del software desarrollado por ustedes. ¿Quién se bajaría?”

Nueve de los diez programadores levantan la mano. El profesor pregunta al restante: “¿Y usted, por qué se quedaría en el avión?”

El programador responde: “Si el software fue desarrollado por nosotros dudo que el avión pueda despegar, mucho menos estrellarse”.

El resto, por aquí.

sábado, 15 de agosto de 2009

Mario and Princess Sex Tape.

No se emocionen, es apenas una broma de la gente de College Humor.

Visto en Punto Geek.

viernes, 14 de agosto de 2009

Jueguitos de Viernes: Stackle

Stackle es una mezcla de Anti-Tetris y Yenga que tenía guardado desde hace tiempo. Es adictivo, pero ¡cuidado! termina por crispar los nervios (con esa musiquita).

StackleMás allá de la zona 4 es… no sé, yo no llego ni a ganchos.

jueves, 13 de agosto de 2009

¿Javascript para todo?

Cuando salió Silverlight me entusiasmaron bastante las posibilidades del “chiche nuevo”. Por fin aquellos que nos dedicamos más a la programación que al diseño gráfico tendríamos una alternativa a Flash (demasiado “visual” para mi gusto por el código) para crear interfaces ricas del lado del cliente.

Pero un jugador rezagado cambió todo eso: Javascript. Siempre estuvo ahí, no recuerdo una web sin él, pero en la época del dominio del IE 5 apenas era útil para hacer un par de validaciones y algunos efectos muy menores.

¿Cual era el problema? En ese momento, ninguno. Uno creía que javascript era para eso, y que para hacer cosas más complejas o vistosas estaban Flash u otras tecnologías similares.

Hoy, lo sabemos, el problema era el IE. La gente de Microsoft apuntaba para otro lado y el hecho de que su motor de javascript fuese una carreta no parecía importarles demasiado. Firefox empezó a cambiar el rumbo y desató una carrera en la que el IE resultó un gran perdedor y Javascript, de la mano de Firefox, Safari y Chrome, el ganador absoluto.

Mientras Microsoft intenta desesperadamente poner el IE al día y Silverlight queda relegado al lugar de herramienta para programadores (que ocupa muy dignamente), lejos incluso de la menguante popularidad de Flash, las posibilidades en Javascript son cada vez más amplias. De la mano de prototype, jQuery, mooTools y otros frameworks ya no hay mucho que javascript tenga que envidiarle a Flash o Silverlight en materia de posibilidades.

Y si no me creen vean 16 Impressive Flash-Like Javascript Animation Inspirations, Tutorials and Plugins. La gracia no es tanto la complejidad de lo que se presenta (aunque algunos ejemplos son realmente increíbles), sino la aplicación del lenguaje a la creación de páginas con fuerte acento en el diseño gráfico sin plugins ni descargas adicionales, sólo el viejo y querido trío HTML+CSS+Javascript.

world of merixStudio sorprende por la suavidad del movimiento:

world

arnaud-k.fr utiliza muy sutilmente las posibilidades del plugin de jQuery jparallax:

arnoud

Además de los ejemplos sorprende la cantidad y calidad de librerías para animación, simulación y efectos que han ido apareciendo en estos últimos tiempos. 16 Impressive Flash-Like Javascript Animation Inspirations, Tutorials and Plugins es una muy buena recopilación para tener en cartera. No se lo pierdan.

miércoles, 12 de agosto de 2009

Demos gracias por los malos programadores.

One bad programmer can easily create two new jobs a year.

David Parnas, referenciado en Coding Horror.

…aunque creo que preferiría vivir con de mi propia tasa de problemas por solución (creo que logro mantenerla debajo de 1) que aprovechar las oportunidades laborales creadas por esos (otros) malos programadores.

A veces me dan ganas de sacar el martillo y solucionar todos los bugs de una sola vez (¿debería aplicarlo sobre la computadora o sobre mi cabeza?), pero termino recordando que el software no se arregla ni se termina (los que se terminan son los proyectos), así que lo importante es que todo esté un poco mejor que ayer.

bad_jobs

martes, 11 de agosto de 2009

Curados de espanto.

emergencia No son todos malos hábitos los que adquirimos los programadores en el transcurso de nuestra vida profesional. Para nivelar un poco quiero traer a cuento varias características que casi todos compartimos, y que de seguro estarán presentes en un buen programador… tienen que estar presentes en un buen programador, ya que todas giran alrededor de un tema que es central en el desarrollo de software: los errores.

Todo software no trivial tiene errores (y como siempre digo, la mayoría de los triviales también). Y si por buena ventura un software en particular no los tiene seguro que los requerimientos que le dieron origen sí. Y si por alguna de esas casualidades cósmicas los requerimientos (y por tanto el software) son consistentes en los papeles, seguro que el usuario o el analista o el redactor o el programador se ha equivocado en su interpretación u omitido alguno de ellos con tal mala suerte que el resultado es consistente aunque, por supuesto, erróneo… y en el imposible caso de que todo esté bien… serán las necesidades del cliente las que cambien o las que cambiarán en breve.

Y hay que tener en cuenta que los innumerables errores detectados (ya sea durante el desarrollo o como defectos en el producto) son apenas una pequeña fracción de los que cometemos todos los días… y sin embargo aquí estamos.

Aunque a veces sucede, no es usual que ante un problema un programador se rasgue las vestiduras, se deprima, paralice o caiga presa de delirios apocalípticos. Esto es, básicamente, porque nuestra vida laboral es una eterna sucesión de problemas y soluciones… que generan más problemas.

¿Qué es lo que –al menos creo yo que- he aprendido?

El camino para estar a salvo de los problemas no es evitar que se produzcan (son inevitables) sino tener alternativas.  Para eso existen los resguardos, los procedimientos alternativos, las salidas de emergencia. Prevenir no es sólo prevenir el problema, sino también sus consecuencias.

Los manotazos de ahogado no sirven. La suerte existe, pero es escasa y no se puede confiar en ella. Hay que medir el costo de un intento a ciegas. ¿Podemos hacer una prueba rápida, a ver si funciona? ¿No romperemos algo más en el intento? Bueno, hagámosla. Pero no podemos perder tiempo tirando tiros al aire, la magia no existe y hay que pensar, buscar, investigar, rastrear, no queda otra.

rep-empresa-thumb

Identificar precisamente el problema es siempre el primer esfuerzo. Tratar de arreglar “algo” que no se sabe qué es es sencillamente imposible. Trabajar sobre las consecuencias es una opción para ganar tiempo, pero nunca una solución a mediano o largo plazo.

No se puede resolver un problema de fondo sobre la emergencia. Cuando se presenta una emergencia hay que resolverla (en lo posible rápidamente) para aliviar la presión y poder pensar y resolver el problema de fondo con tranquilidad… Creo que en esto todos fallamos más de una vez, tal vez no tanto en la resolución rápida, sino en la disolución de la causa subyacente. ¿Cuántas veces dejamos parches provisorios como soluciones “definitivas” que no tardan en crear más y más graves apuros?

Presionando no se resuelve nada, por lo menos en sistemas. No es de mucha ayuda gritarle lo obvio al administrador de la red –que todas las terminales están fuera de servicio o que el mundo va a explotar-. Si encuentra la solución en un tiempo razonable será a pesar de ello y no gracias a ello. Ya habrá tiempo de repartir responsabilidades y amenazas luego, hay que concentrarse en buscar una solución.

Hay que dejar trabajar. Si nos es posible paliar las consecuencias o aliviar la presión, ésa es la forma de ayudar. Y si no está entre nuestras posibilidades hacerlo… mucho ayuda el que no estorba, dice el refrán.

Aprender por prueba y error implica resolver, aunque sea parcialmente, problemas de fondo. De cada problema, de cada situación, tenemos que quedarnos con alguna mejora. Puede ser pequeña, tal vez no una solución completa, tal vez apenas una alerta temprana para la próxima vez, algo, lo que sea. Si vivimos en un interminable devenir de emergencias, la única forma de salir de ello es de a poco, aprovechando el poco tiempo que nos queda para ganar más tiempo.

lunes, 10 de agosto de 2009

Señales de que eres un mal programador.

Más allá de los muy buenos chistes que contiene y del tono jocoso en general, Signs that you're a bad programmer es un artículo imperdible que debe tomarse en serio.

Está dividido en tres partes: “Señales de que eres un mal programador”, “Señales de que eres un programador mediocre” y “Señales de que no deberías ser un programador”. En cada una de ellas se presentan estas “señales” con sus respectivos síntomas y consejos para superarlas (carreras alternativas, para el último caso).

Aunque la gravedad de cada una en particular es discutible (yo no calificaría de “malo” a un programador con dificultades para seguir la recursión, me parece lo suficientemente complicado como para marearse sin merecer un calificativo tan extremo) es innegable su utilidad para identificar problemas y puntos débiles en uno mismo y los demás (o para decidirse a cambiar de carrera).

Más que puntear las que me parecen más importantes, voy a mencionar los síntomas que me resultan más molestos:

zombie "voodoo (o zombie) code"

Mantener código que no hace al objetivo del programa: inicializar variables que luego no se utilizan, llamar a funciones que no hacen nada o cuyo resultado es descartado, producir información que finalmente no se muestra.

Esto hace que seguir el código sea exasperante y su corrección peligrosa, ya que uno tiene que ir separando la paja del trigo para entenderlo, corriendo el riesgo de sacar algo que sí servía. Una solución posible es no limpiar nada, lo que nos lleva a…

noloborres “no lo borres”

Con lo que me gusta borrar código… Esto me molesta sobre todo cuando aparece en la forma de cierta tendencia -que veo incluso en programadores experimentados- a llevar el control de cambios en medio del código mismo (¿costumbres de una época en la que el uso de sistemas de control de código fuente no era tan común?). Me refiero a éste tipo de comentarios:

“J.P. 25/7/2006: arreglo del incidente nro. XXX. El cálculo del total de…”

Estoy tratando de entender que hace el código ahora, y no me interesa saber qué hacía hace tres años. Para hacer investigación histórica está el historial de cambios. A la basura.

Pero… por algo debe estar ahí, ¿por qué mejor no lo dejamos?…

candados “por las dudas”

Código que se ejecuta “para estar seguro de…” O estamos seguros y no hay que asegurarse o no estamos seguros y hay que molestarse en entender hasta estarlo, ¿no? ¿Seguro que Trim borra los espacios a los dos lados de la cadena? Mmmm… mejor hagamos miCadena=miOtraCadena.LTrim().RTrim()… por si acaso. O escribamos (int)4, no sea cosa que… Para asegurarse está la ayuda, no el código.

Una variación sutil de lo anterior es arreglar un problema en la salida de una función manoseando el código que la consume, “por las dudas” de que “alguien más” la esté utilizando. ¿Alguien más la está utilizando? ¿Tan difícil es ir, fijarse en dónde y en todo caso corregir esos otros lugares? Esto me lleva a un punto que me parece que falta en el artículo referenciado:

anteojeras “programar con ateojeras”

¿Vieron las anteojeras que se le ponen a los animales de tiro para que vayan siempre para adelante sin asustarse con lo que pasa a su alrededor? Hay gente que programa con eso puesto. Si tienen que corregir el incidente n que aparece en la pantalla x van a corregir eso y solamente eso, indiferentes a cualquier otro problema, por más grueso que sea.

incisión Son como cirujanos que acceden al código y extirpan un tumor con precisión milimétrica… sin darse cuenta de que hace tiempo que el paciente está muerto. Finalmente el aporte de estas acciones al proyecto en general es tanto como su alcance… milimétrico.

Bueno, hay mucho más… mejor lean el artículo.

sábado, 8 de agosto de 2009

Metáforas sobre el desarrollo de software: el juego de ajedrez.

ajedrez  La complejidad del un proceso de desarrollo de un software es exponencialmente proporcional a la complejidad del producto (digo, a ojo de buen cubero).

Las metáforas nos ayudan a manejarnos en la complejidad sin sentirnos desbordados, creo que es por esto que abundan tantas y tan variadas acerca del desarrollo de software, del trabajo en equipo o de las relaciones laborales

En Qué quieres desarrollar hoy? me encontré con una muy original y acertada: La programación es como un juego de ajedrez. ¿Se consideran buenos jugadores? Yo creo que me defiendo, aunque algunas veces me siento como Felipe:ajedrez2

viernes, 7 de agosto de 2009

Jueguitos de viernes: Typing Maniac.

Por primera vez en esta sección un juego de Facebook. Typing Maniac es uno muy (pero muy) adictivo para mejorar nuestra velocidad de tipeo. Simplemente tenemos que escribir las palabras a medida que van cayendo… cada vez más rápido. Es imposible llegar lejos sin un poco de técnica de juego adicional (pueden hacer una búsqueda en google).

TypingManiac

(Otra vez fue @Cerebrado el que descubrió esta cosa nefasta que hizo que mis posts de .Net + jQuery salieran una semana tarde.)

miércoles, 5 de agosto de 2009

Frases: (diferentes tipos de) monitos con navaja.

The three most dangerous things in the world are a programmer with a soldering iron, a hardware engineer with a software patch, and a user with an idea.

- The Wizardry Compiled by Rick Cook

Visto en Soft Cero.

martes, 4 de agosto de 2009

Ajax: C# .Net 3.5 + jQuery (VI) – Proxies de objetos para javascript, el lado del cliente.

Con esta entrada finalizamos esta serie en la que hemos prototipado diferentes proxies en javascript: para llamar a métodos web, a servicios, y para utilizar objetos de C# “como si fueran de javascript”.

Para el final de la entrada anterior teníamos resuelto un servicio (InvokerService.asmx en nuestro código de ejemplo) que nos permitía recibir un objeto de cualquier tipo en JSON, deserializarlo, ejecutar un método indicado por un parámetro y devolver tanto el resultado del método como una representación del estado final del objeto. Quedó entonces pendiente para esta entrada la parte del cliente, es decir, un objeto javascript que “simule” ser ese objeto de C#.

Creo que lo más fácil es primero hacer un proxy para un tipo específico a mano y luego utilizar ese código como modelo para codificar un procedimiento que genere un proxy similar, pero ya tomando el tipo como parámetro.

Recordemos lo que este proxy debe hacer:

  • Al invocar un método en este objeto de javascript, internamente  pasa la llamada a un único web service (InvokerService.asmx) que recibe el objeto serializado, el tipo equivalente en C# y la orden de ejecutar un método con los parámetros correspondientes, que también recibe serializados.
  • El servidor devuelve el resultado del método y el estado final del objeto serializado, ya que esa ejecución pudo haber modificado alguna de sus propiedades.
  • De vuelta en el cliente copiamos las propiedades del objeto que recibimos a sus correspondientes en el de javascript y devolvemos el resultado del método.

Empecemos entonces con el código. Buen momento para recordarles que la solución de VS2008 completa está subida a Google Code.

Para que la creación del proxy por código sea más sencilla tenemos que trasladar toda la funcionalidad que podamos fuera del proxy, a una función estática que podamos incluir en nuestro archivo JSProxy.js.

Así, vamos a crear primero una función auxiliar, Page.Ajax.Invoke, que es la que hará la mayor parte del trabajo:

Page.Ajax.Invoke = function(type,obj,method)
{
    var params = null;
    
    if(arguments.length > 3)
    {
        var params = new Array();
        for(var i=3;i<arguments.length;i++)
            params.push(JSON.stringify(arguments[i]));
    }
    
    //función para pasar a JSON.stringify que excluye __type__ de la serialización del 
    //objeto ya que el tipo se pasa en type.
    var excludeType = function(key,value){
        if(key!="__type__")
            return value;                                
    };
    
    var jsonObj = Page.Utils.JSON.stringify(obj, excludeType);
    var jsonParams=Page.Utils.JSON.stringify(params);

    var ret = Page.InvokerService.Invoke(type, jsonObj, method, jsonParams);

    for(var i in ret.TargetObject)
        obj[i] = ret.TargetObject[i];
    
    return ret.ReturnValue;                            
}

La declaración de esta función es un poco rara, porque la complica el hecho de que no sabemos cuántos parámetros requiere el método a invocar. Podríamos pasar un array, pero es más fácil para la codificación si los pasamos como argumentos adicionales, accediéndolos a través del array especial de javascript arguments.

Así, sólo están especificados los tres primeros parámetros, que son siempre los mismos: type (el nombre del tipo en C# a invocar), obj (una referencia al proxy javascript que inicia la invocación) y method (el método a invocar). A partir de allí, debemos pasarle los parámetros para el método a invocar, tantos como sean necesarios. ¿Enrevesado? Es más fácil codificarlo que decirlo, créanme.

Tomemos como modelo la clase Calc, el ejemplo de nuestro post anterior:

namespace AjaxConJQueryObjectProxy
{
    public class Calc
    {
        public Calc() { }

        public decimal Op1 { get; set; }
        
        public decimal Op2 { get; set; }

        public decimal Sum()
        {
            this.Op1 = this.Op1 + this.Op2;
            return this.Op1;
        }

        public decimal SumNumbers(decimal op1, decimal op2)
        {
            this.Op1 = op1;
            this.Op2 = op2;

            return this.Sum();
        }
    }
}

El proxy javascript queda como sigue:

Page.Ajax.Namespace("AjaxConJQueryObjectProxy");

AjaxConJQueryObjectProxy.Calc = function(){
   this.__type__ = "AjaxConJQueryObjectProxy.Calc";        

   this.Op1 = 0;
   this.Op2 = 0;

   this.Sum = function()
   {
       return Page.Ajax.Invoke(this.__type__, this, "Sum");
   }

   this.SumNumbers = function(op1, op2)
   {
       return Page.Ajax.Invoke(this.__type__, this, "SumNumbers", op1, op2);
   }
}

Como verán, quedó bastante simple. Es apenas un pasamanos estético entre el programador final y Page.Ajax.Invoke. Con eso como guía, vamos a crear la función estática ImportObject en nuestra clase JSProxy.

public static StringBuilder ImportObject(Type type, string executeServiceUrl)
{
    StringBuilder builder = new StringBuilder(400);
    builder.Append("$().ready(function(){");

    //crea el namespace.
    if (type.FullName.Contains('.'))
    {
        string ns = type.FullName.Substring(0, type.FullName.LastIndexOf('.'));
        builder.AppendFormat("Page.Ajax.Namespace(\"{0}\");", ns);
    }

    //declaración.
    builder.AppendFormat("{0}=function(){{", type.FullName);
    //variable interna con el nombre del tipo que representa.
    builder.AppendFormat("this.__type__=\"{0}\";", type.FullName);

    //inicialización de propiedades. Hay que crear una instancia de type
    //para determinar sus valores por defecto.
    object instance = type.Assembly.CreateInstance(type.FullName);

    FieldInfo[] fields = type.GetFields(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static);
    JavaScriptSerializer j = new JavaScriptSerializer();
    foreach (FieldInfo field in fields)
    {
        builder.AppendFormat("this.{0}=Page.Utils.JSON.parse(\"", field.Name);
        j.Serialize(field.GetValue(instance), builder);
        builder.Append("\");");
    }

    //si bien el tratamiento de las propiedades es igual al de las variables públicas (fields)
    //tal vez haya que hacer algo separado más adelante.
    PropertyInfo[] properties = type.GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static);
    foreach (PropertyInfo property in properties)
    {
        builder.AppendFormat("this.{0}=Page.Utils.JSON.parse(\"", property.Name);
        j.Serialize(property.GetValue(instance, null), builder);
        builder.Append("\");");
    }

    //métodos.
    MethodInfo[] methods = type.GetMethods(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static);
    foreach (MethodInfo method in methods)
    {
        if (method.IsSpecialName)
            continue;

        //declaración
        builder.AppendFormat("this.{0}=function(", method.Name);
        //parámetros.
        ParameterInfo[] parameters = method.GetParameters();
        if (parameters.Length > 0)
        {
            foreach (ParameterInfo parameter in parameters)
                builder.AppendFormat("{0},", parameter.Name);
            builder.Remove(builder.Length - 1, 1);
        }
        builder.Append("){");
        //llamada interna a Page.Ajax.Invoke
        builder.AppendFormat("return Page.Ajax.Invoke(this.__type__, this, \"{0}\",", method.Name);
        //pasaje de parametros a Page.Ajax.Invoke
        if (parameters.Length > 0)
        {
            foreach (ParameterInfo parameter in parameters)
                builder.AppendFormat("{0},", parameter.Name);
        }
        builder.Remove(builder.Length - 1, 1);
        builder.Append(");");  //fin de la llamada a Page.Ajax.Invoke

        builder.Append("};");// fin del método.
    }

    builder.Append("};"); //fin del objeto.

    builder.Append("});"); //fin de $().ready( function(){
    return builder;
}

Lo único que hace es crear en un StringBuilder un código similar al que vimos más arriba, examinando con reflection el tipo que se le pasa como parámetro. El ajuste fino fue trabajoso… falta una coma, sobra un paréntesis, pero requiere más paciencia que inteligencia.

Vamos a probar. En el código C# de default.aspx incluimos la llamada a JSProxy.ImportObject que nos devuelve el javascript para el proxy de Calc, y lo incluye en el header de la página. El código debe verse así:

using System;

namespace AjaxConJQueryObjectProxy
{
    public partial class _Default : PageBase
    {
        protected override void OnPreLoad(EventArgs e)
        {
            base.OnPreLoad(e); 
            
            //AddHeaderScript es una función que en el ejemplo habíamos puesto como privada
            //de PageBase. Agrega un tag <script> en el header con el código que se le pasa.
            //Es muy útil, así que si la hacemos protected la podemos utilizar en todas las
            //páginas.
            string calcImport = JSProxy.ImportObject(typeof(Calc), "/InvokerService.asmx").ToString();
            base.AddHeaderScript(calcImport);           
        }
    }
}

Sin nada más que eso, ya podemos probar nuestro objeto Calc en javascript con una pequeña función de prueba:

function TestCalculadora()
{    
    var calc1 = new AjaxConJQueryObjectProxy.Calc();
    var calc2 = new AjaxConJQueryObjectProxy.Calc();
    
    calc1.Op1 = 3;
    calc1.Op2 = 7;
    
    calc2.Op1 = 1;
    calc2.Op2 = 4;
    
    alert(calc1.Sum()); //devuelve (operador1 = 3) + (operador2 = 7) = 10 => operador1
    alert(calc2.Sum()); //devuelve (operador1 = 1) + (operador2 = 4) = 5 => operador1
    alert(calc1.Sum()); //devuelve (operador1 = 10) + (operador2 = 7) = 17 => operador1
    alert(calc2.Sum()); //devuelve (operador1 = 5) + (operador2 = 4) = 9 => operador1                
};

¡Y –con suerte- funciona! Les recuerdo que el código completo está en Google Code.


Esta serie de posts son una especie de puesta al día de las ideas que se plasmaron en la arquitectura que utilizaba en mi trabajo en ElectroChance, la gran mayoría de ellas pergeñadas y/o recolectadas por Rick Hunter (por más que te cambies el nombre sabemos quién sos en realidad -sigan el link-), así que a él especialmente y a todos los que alguna vez conformaron ese dream-team, gracias.

lunes, 3 de agosto de 2009

Ajax: C# .Net 3.5 + jQuery (V) – Proxies de objetos para javascript, un servicio de ejecución.

Haciendo un recuento de las herramientas que estuvimos probando en las entradas anteriores de esta serie me imaginaba cómo sería a la hora de desarrollar una funcionalidad:

  • Creamos las funciones y procedimientos específicos para la comunicación entre cliente y servidor en el código C# de la (o las) página y los marcamos con WebMethod y ScriptMethod:
using System;
using System.Web.Script.Services;
using System.Web.Services;

namespace AjaxConjQueryJSProxy
{
    public partial class _Default : PageBase
    {
        [WebMethod]
        [ScriptMethod(ResponseFormat = ResponseFormat.Json)]
        public static int Sum(int a, int b)
        {
            return a + b;
        }
    }
}
  • Sin hacer nada más que heredar la página de nuestra PageBase podemos utilizarlas desde javascript haciendo
var suma = Page.Sum(2,5);
  • Algo similar podemos hacer con funciones que consumimos desde un servicio Web. Tenemos que “registrarlo” en algún evento del ciclo de vida de la página, por ejemplo en el PreLoad:
using System;

namespace AjaxConJQueryObjectProxy
{
    public partial class _Default : PageBase
    {
        protected override void OnPreLoad(EventArgs e)
        {
            base.OnPreLoad(e); 
            /* AddHeaderScript es una función que en el ejemplo habíamos puesto como privada
            de PageBase. Agrega un tag <script> en el header con el código que se le pasa.
            Es muy útil, así que si la hacemos protected la podemos utilizar en todas las
            páginas. */
            base.AddHeaderScript(JSProxy.ImportService(typeof(WebService1), "WebService1.asmx").ToString());           
        }
    }
}
  • y sin más lo podemos utilizar desde javascript haciendo:
Page.WebService1.HelloWorld()

Bastante bien, pero… soy un tipo vago, muy vago. Estaba pensando que podría, en vez de tener muchos servicios y métodos, hacer algo que me permita codificar una clase común y corriente en C# y utilizarla directamente en javascript.

Digamos que tengo la clase Calc, que se comporta como una calculadora: tiene dos operadores y al efectuar una operación devuelve el resultado al tiempo que lo coloca en el primer operador, que hace de acumulador. También puede tener un método-atajo al que le paso los dos operadores, establece las propiedades y efectúa la operación en un sólo paso.

No tiene sentido darle mucha funcionalidad. Mi calculadora de ejemplo sólo sabe sumar:

namespace AjaxConJQueryObjectProxy
{
    public class Calc
    {
        public Calc() { }

        public decimal Op1 { get; set; }
        
        public decimal Op2 { get; set; }

        public decimal Sum()
        {
            this.Op1 = this.Op1 + this.Op2;
            return this.Op1;
        }

        public decimal SumNumbers(decimal op1, decimal op2)
        {
            this.Op1 = op1;
            this.Op2 = op2;

            return this.Sum();
        }
    }
}

Las propiedades Op1 y Op2 son los operadores, el método Sum los suma, devuelve el resultado y lo coloca en Op1. Por otro lado el “atajo” SumNumbers recibe los operadores como parámetros y luego de pasarlos a las propiedades correspondientes llama a Sum devolviendo el resultado.

Ahora, ¿por qué no puedo, en javascript y sin trabajo adicional (sin web services, web methods, ni nada por el estilo), hacer…

var calc1 = new AjaxConJQueryObjectProxy.Calc();

//prueba Sum
calc1.Op1 = 3;
calc1.Op2 = 7;
var res = calc1.Sum();
alert(res);

//prueba SumNumbers
res = calc1.SumNumbers(5,6);
alert(res);

…? ¿Eh? ¿Por qué?

Antes de empezar con todas estas pruebas hubiese dicho que es posible pero mucho más trabajoso que la comodidad que aporta, ya que al fin y al cabo llamar a un método web que haga lo mismo es muy fácil. Pero ahora estamos a un par de pasitos de conseguirlo con muy poco código:

  1. Creamos un objeto en javascript con las mismas propiedades que su equivalente en C#. Esto lo podemos hacer utilizando reflection en una forma similar a Automatizando la creación de proxies de javascript e Importando un proxy javascript.
  2. Al invocar un método en este objeto de javascript, internamente se invoca a un único web service que recibe el objeto serializado, el tipo equivalente en C# y la orden de ejecutar un método con los parámetros correspondientes, también serializados.
  3. El servidor crea una instancia del objeto equivalente en C# y ejecuta el método, devolviendo el resultado y el objeto serializado, ya que la ejecución pudo haber modificado propiedades.
  4. De vuelta en el cliente copiamos las propiedades del objeto que recibimos a sus correspondientes en el de javascript y devolvemos el resultado del método.

Hasta ahora, lo único que no tenemos es un servicio web “genérico” que pueda instanciar cualquier objeto y ejecutar cualquier método que se le solicite, así que es lo que vamos a hacer.

La firma del método sería:

[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public MethodExecuteResult Invoke(string typeName, string objectData, string methodName, string methodParams)

Invoke recibe los parámetros como string ya que no se sabe, a priori, a qué tipo corresponden: objectData (el objeto javascript serializado en JSON) debe convertirse en una instancia del tipo que indique typeName, y los elementos de methodParams (un array de parámetros serializado en JSON) deben convertirse al tipo de los parámetros del método. No podemos dejar esta conversión al framework ya que, por ejemplo, un 3 se convertiría a Int32 y tal vez el método requiere un Decimal.

El retorno es una clase sencilla que tiene como propiedades el objeto deserializado y el valor de retorno del método:

public class MethodExecuteResult
{
    public object TargetObject { get; set; }
    public object ReturnValue { get; set; }
}

Vamos primero por la conversión de objectData, para la que utilizaremos el método JavaScriptSerializer.Deserialize<T>. El problema aquí es que es un método genérico, y nosotros tenemos el tipo <T> como parámetro (¿por qué no hay una sobrecarga con el tipo como parámetro?). Hay dos formas de resolver este tipo de problemas: fácil y rápida de codificar o difícil y rápida al ejecutar. La primera es utilizar reflection para invocar al método genérico, y la segunda es tomar el Reflector y “robarse” el código necesario del framework, modificando el método luego… creo que a los efectos de esta prueba (y de casi toda la vida) la primera técnica será suficiente, y es bastante sencilla:

private static object JavascriptDeserialize(Type type, string objectData)
{
    MethodInfo methodInfo = typeof(JavaScriptSerializer).GetMethod("Deserialize");
    Type[] genericArguments = new Type[] { type };
    MethodInfo genericMethodInfo = methodInfo.MakeGenericMethod(genericArguments);
    return genericMethodInfo.Invoke((new JavaScriptSerializer()), new object[] { objectData });
}

Ahora que tenemos un método para deserializar JSON forzando el resultado a un tipo específico, todo es mucho más fácil. Puse los comentarios en el código:

[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public MethodExecuteResult Invoke(string typeName, string objectData, string methodName, string methodParams)
{
    //obtiene el tipo del objeto a invocar a partir de typeName
    Type type = Type.GetType(typeName);

    //obtiene la información del método a invocar a partir de methodName
    MethodInfo methodInfo = type.GetMethod(methodName);
    //obtiene la información de los parámetros del método a invocar.
    ParameterInfo[] methodParamsInfo = methodInfo.GetParameters();

    //si el método a invocar tiene parámetros obtiene los valores
    //a partir de methodParams, que es un array de strings con un parámetro serializado en json por posición.
    object[] paramValues;
    if (methodParamsInfo.Length > 0)
    {
        string[] paramValuesRaw = (string[])InvokerService.JavascriptDeserialize(typeof(string[]), methodParams);
        paramValues = new object[methodParamsInfo.Length];
        for (int paramIndex = 0; paramIndex < methodParamsInfo.Length; paramIndex++)
            paramValues[paramIndex] = InvokerService.JavascriptDeserialize(methodParamsInfo[paramIndex].ParameterType, paramValuesRaw[paramIndex]);
    }
    else
        paramValues = new object[] { };

    //obtiene una instancia del objeto a invocar a partir de objectData.
    object targetObject = InvokerService.JavascriptDeserialize(type, objectData);

    //invoca al método y obtiene el resultado.
    BindingFlags b = BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.Public;
    object methodReturnValue = type.InvokeMember(methodName, b, null, targetObject, paramValues);

    //crea el valor de retorno incluyendo el resultado de la invocación del método
    //y el objeto sobre el que se invocó serializado.
    return new MethodExecuteResult() { ReturnValue = methodReturnValue, TargetObject = targetObject };
}

Y listo, ya tenemos nuestro servicio de ejecución on the fly. Ahora sólo queda lo más fácil, hacer el proxy. Seguimos en la próxima.


Esta serie de posts son una especie de puesta al día de las ideas que se plasmaron en la arquitectura que utilizaba en mi trabajo en ElectroChance, la gran mayoría de ellas pergeñadas y/o recolectadas por Rick Hunter (por más que te cambies el nombre sabemos quién sos en realidad -sigan el link-), así que a él especialmente y a todos los que alguna vez conformaron ese dream-team, gracias.

sábado, 1 de agosto de 2009