tag:blogger.com,1999:blog-19516924532950437662024-03-05T09:53:07.208-03:00@diegoDiego Fernándezhttp://www.blogger.com/profile/11282484801526539091noreply@blogger.comBlogger58125tag:blogger.com,1999:blog-1951692453295043766.post-38608043725688033062012-08-14T14:14:00.000-03:002012-08-14T14:14:49.499-03:00Jugando con jQuery Deferred<br />
El objeto <a href="http://api.jquery.com/category/deferred-object/">Deferred</a> es una característica poco conocida de jQuery que permite escribir menos código. Veamos como aprovecharlo.<br />
<br />
<div>
Todas las funciones AJAX en jQuery retornan un Deferred, y por lo tanto:</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">$.ajax({url:'/api/users', success:function(d){console.log(d)}});</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
se puede escribir como:<br />
<br />
<div>
<span style="font-family: Courier New, Courier, monospace;">$.ajax({url:'/api/users'}).done(function(d){console.log(d)})</span><br />
<br />
El cambio es sutil pero simplifica algunos casos engorrosos. Por ejemplo esperar el resultado de multiples llamadas AJAX:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">var usersRequest = $.get('/api/users');<br />var settingsRequest = $.get('/api/settings');<br /><br />jQuery.when(usersRequest, settingsRequest).done(function (users, settings) {<br /> console.log(users);<br /> console.log(settings);<br />});</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
O simplificar código mediante null objects:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">var request = (shouldUpdate) ? $.post('/api/user', userData) </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> : jQuery.Deferred().resolve();<br />request.done(function() { doSomething() });</span></div>
</div>
Diego Fernándezhttp://www.blogger.com/profile/11282484801526539091noreply@blogger.com0tag:blogger.com,1999:blog-1951692453295043766.post-80693350924581453342012-04-04T11:34:00.004-03:002012-04-04T11:41:38.639-03:00Amber SmalltalkDespués de mucho tiempo volvi a ver el blog de <a href="http://www.jarober.com/">James Robertson</a>, donde me encontre con <a href="http://amber-lang.net/">Amber Smalltalk</a>: una implementación de Smalltalk hecha en JavaScript.<br />
<div>
<br /></div>
<div>
Seguramente la pregunta práctica es: ¿A quien le puede interesar interpretar Smalltalk con JavaScript?</div>
<div>
<br /></div>
<div>
Más alla de la respuesta obvia de: a los fanáticos de Smalltalk; hay algunos buenos casos donde Amber resulta interesante:</div>
<div>
<br /></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><modalidad-fanatico-smalltalk></span></div>
<div>
<ul>
<li><b>Enseñanza:</b> Uno puede incrustar un classbrowser junto con una pagina tutorial, y usarlo para dar clases de programación orientada a objetos: los alumnos solo necesitan un browser.</li>
<li><b>Producción:</b> Si existiesen las herramientas apropiadas, uno podría usar un entorno de objetos para desarrollar y luego exportar todo a un JavaScript minimizado y listo para usar con <a href="http://nodejs.org/">NodeJS</a> o en un browser. (El gran obstáculo: las diferencias de sintaxis JavaScript/Smalltalk, <a href="http://amber-lang.net/documentation.html#JSObjectProxy">JObjectProxy</a> luce feo para quienes están acostumbrados al <a href="http://www.jquery-tutorial.net/introduction/method-chaining/">encadenamiento de mensajes en jQuery</a> o el syntax sugar de <a href="http://coffeescript.org/">CoffeScript</a>).</li>
</ul>
</div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"></modalidad-fanatico-smalltalk></span></div>
<div>
<br /></div>
<div>
De vuelta en mi modalidad realista.</div>
<div>
<br /></div>
<div>
Por ahora es un lindo experimento para ver, que demuestra una vez más como JavaScript se esta convirtiendo en el "assembler" de la web. </div>
<div>
<br /></div>
<div>
Hay dos características de Smalltalk que lo convierten en un lenguaje unico, y son también su maldición:</div>
<div>
<ol>
<li><b>El "ambiente de objetos"</b>. Para que cualquiera que haya trabajado un tiempo considerable con Smalltalk volver a los archivos y las herramientas de build es como volver a la pre-historia. <br />Sin embargo es una maldición por que aparta al lenguaje de todo el resto del mundo: usar herramientas existentes de versionado o editores comunes se vuelve doloroso y poco práctico.</li>
<li><b>La sintaxis de message keywords</b> es una camino de ida :) y es una de las cosas que más extraño en otros lenguajes orientados a objetos: realmente ayuda a pensar en términos de objeto/mensaje. <br />El lado negativo es que la diferencia de sintaxis complica la interoperabilidad con lenguajes tipo C como JavaScript. <br />Creo que en este sentido los smalltalkers fueron muy conservadores: la sintaxis de Smalltalk es uniforme y genera cierto orgullo. Orgullo que debería dejarse de lado: las características menos elegantes de Groovy o Ruby resultan mucho más prácticas.</li>
</ol>
<div>
Mientras tanto voy observando la evolución de <a href="http://www.dartlang.org/">Dart</a>, quien sabe quizas con sus <a href="http://www.infoq.com/articles/google-dart">snapshots</a> logre lo que Smalltalk no pudo: lograr una tregua entre el ambiente de objetos y las herramientas basadas en archivos.</div>
</div>Diego Fernándezhttp://www.blogger.com/profile/11282484801526539091noreply@blogger.com0tag:blogger.com,1999:blog-1951692453295043766.post-867215425997497342012-02-17T22:05:00.000-03:002012-02-17T22:05:06.127-03:00Setup de una aplicación web java en 3 lineasA veces necesito hacer pequeños proyectos web con Java, quizás para probar algo a aprender un API. El problema -común en Java- es que hasta lo más simple requiere muchos pasos de configuración.<br />
<br />
Algunas alternativas para hacer rapidamente un proyecto web son Maven y sus "arquetypes", NetBeans, Eclipse o Idea con los plugins correctos y un Tomcat instalado.<br />
<br />
En este post quería contarles una alternativa que me resulta muy sencilla y util, ahi va la receta:<br />
<br />
<b>1.</b> Instale <a href="http://groovy.codehaus.org/">Groovy</a> y <a href="http://gradle.org/">Gradle</a> (solo es necesario hacerlo una vez).<br />
<br />
<b>2.</b> Cree un directorio para el projecto siguiendo la misma convención de Maven:<br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;">mkdir -p prueba/src/main/webapp</span><br />
<br />
<b>3.</b> Agregue una pizca de Gradle<br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;">echo 'apply plugin: "jetty"' > prueba/build.gradle</span><br />
<br />
<b>4.</b> Cocine:<br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;">cd prueba && gradle jettyRun</span><br />
<br />
Ok, es una aplicación web vacía, pero podes agregar los HTML que necesites dentro de src/main/webapp (si usaste Maven la convención de directorios de Gradle es la misma).<br />
<br />
Ahi va todo junto para que puedas hacer copy & paste (técnicamente son "4 lineas" pero el número 3 quedaba mejor para el titulo :) ):<br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;">mkdir -p prueba/src/main/webapp</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">echo 'apply plugin: "jetty"' > prueba/build.gradle</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">cd prueba && gradle jettyRun</span><br />
<br />
... una cosita más...<br />
<br />
Si necesitas usar Eclipse o Idea, agrega estas lineas a build.gradle:<br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;">apply plugin: 'eclipse'</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">apply plugin: 'idea'</span><br />
<br />
Después ejecutas:<br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;">gradle eclipse idea</span><br />
<br />
y listo!Diego Fernándezhttp://www.blogger.com/profile/11282484801526539091noreply@blogger.com0tag:blogger.com,1999:blog-1951692453295043766.post-37497014431581678772012-02-03T11:29:00.002-03:002012-02-04T11:49:15.848-03:00Tips de Chrome para desarrolladores webTrabajando en unas páginas web, descubrí una funcionalidad de Google Chrome que lo convierte en mi browser favorito para el desarrollo web.<br />
<br />
Suelo retocar y probar cambios de CSS en el "inspector" de la solapa "Elements". El problema es copiar los cambios de vuelta al código fuente: no solo es incomodo, si no que a veces me olvido que elementos cambie.<br />
<br />
En todo este tiempo no le había dado importancia a la solapa "Resources". La usaba de vez en cuando para ver que un archivo no se se este cargando del cache y cosas por el estilo.<br />
<br />
Ayer por casualidad descubrí que los archivos se pueden editar directamente (haciendo click en el botón del lápiz que pueden ver en la imagen).<br />
<br />
Pero lo bueno recién comienza: si hacen cambios estos se ven de forma instantánea en la pagina; si "graban" (Ctrl-S o Cmd-S en Mac) van a poder ver la historia en el editor que además muestra los cambios con colores.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDXSuCQK6SbvZNYoddu_5oDv-NkxUg-ypk8u8L28ACGc0WgQaO7xde9K-DOhv3yNf5jQHT9bwkV_PS5deHbtzIOvBvHjefLTPOVFhu15vukq4GJUasmDKFq8orINNwB5Gptiiz37o_27Fb/s1600/Screen+Shot+2012-02-03+at+11.06.01+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="305" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDXSuCQK6SbvZNYoddu_5oDv-NkxUg-ypk8u8L28ACGc0WgQaO7xde9K-DOhv3yNf5jQHT9bwkV_PS5deHbtzIOvBvHjefLTPOVFhu15vukq4GJUasmDKFq8orINNwB5Gptiiz37o_27Fb/s640/Screen+Shot+2012-02-03+at+11.06.01+AM.png" width="640" /></a></div>
<br />
<br />
Obviamente el archivo no se guarda en el servidor, pero haciendo botón derecho pueden hacer un "Save As..." y guardarlo.<br />
<br />
Probablemente no sea algo nuevo, pero descubrirlo me ahorro tanto tiempo que me dieron ganas de bloggearlo :)<br />
<br />
<b>Nota:</b> <a href="http://getfirebug.com/">Firebug</a> también tiene una funcionalidad similar, aunque sin historial de cambios y con una UI un poco más incomoda a la hora de editar.Diego Fernándezhttp://www.blogger.com/profile/11282484801526539091noreply@blogger.com0tag:blogger.com,1999:blog-1951692453295043766.post-14098423102380540032011-12-22T00:03:00.002-03:002011-12-22T00:03:42.706-03:00"Developability"Existen numerosos atributos de calidad, e incluso <a href="http://en.wikipedia.org/wiki/ISO/IEC_9126">clasificaciones estándar de ellos</a>.<br />
<br />
¿Para que sirven estas clasificaciones?<br />
<br />
Por un lado sirven para darle a los diseñadores de sistemas un punto de partida. Algo que permita dar respuesta a la pregunta: "entre las características que requiere nuestro sistema ¿nos estaremos olvidando de algo?". Por otro sirven para que los investigadores de arquitectura de software puedan definir métricas y <a href="http://en.wikipedia.org/wiki/Software_architecture#Examples_of_architectural_styles_and_patterns">estilos de solución</a>.<br />
<br />
Y por otro, sirven para que yo tenga una excusa por donde empezar este post :)<br />
<br />
Estaba pensando que en estas clasificaciones hay un atributo de calidad perdido, que voy a bautizar como "<a href="http://www.merriam-webster.com/dictionary/developability">Developability</a>", y voy a definir así: <i>la capacidad un sistema para reflejar los cambios introducidos durante el desarrollo</i>.<br />
<br />
Después de todo si en Java existen productos como <a href="http://zeroturnaround.com/jrebel/">JRebel</a> y frameworks como <a href="http://www.playframework.org/">Play</a> anuncian sus bondades con frases como: <i>"No need to compile, deploy or restart the server";</i> creo que existe una necesidad de tener la característica de "developability" en mente a la hora de pensar en el diseño de un sistema.<br />
<br />
<b>¿No es lo mismo que Maintainability?</b><br />
Es parte de la mantenibilidad, en el sentido que forma parte de la facilidad de cambio de un sistema.<br />
<br />
Pero muchas veces cuando se habla de mantenibilidad se hace referencia a cuan fácil es introducir nueva funcionalidad sin tener que hacer grandes cambios en el diseño.<br />
<br />
Podemos tener, por ejemplo, un sistema muy fácil de extender, donde cada vez que un desarrollador introduzca un cambio tenga que esperar 2hs a que termine la compilación para poder verlo.<br />
<br />
<b>¿No es lo mismo que Testablity?</b><br />
<a href="http://en.wikipedia.org/wiki/Software_testability">Testabilty</a> se refiere a que tan fácil es verificar el software, por ejemplo: si es posible realizar test automatizados con facilidad o no.<br />
<br />
Y lo que planteo es en cierto sentido similar, pero con objetivos levemente distintos: un sistema puede ser fácil de testear en el sentido que es posible automatizar tests u obtener información de estado relevante para el testing y aún así ser un tremendo "dolor" para desarrollar.<br />
<br />
<b><span style="font-size: large;">Un ejemplo: Mejorando el "developability" de Microwiki</span></b><br />
Mi pequeño proyecto: <a href="https://github.com/dfernandez79/microwiki">microwiki</a>; del que les conte hace <a href="http://diegacho.blogspot.com/2011/11/microwiki-primeros-pasos-de-diseno.html">un par de posts atras</a>. Utiliza templates de html para renderizar las paginas web. Estos templates son resources y se cargan del classpath.<br />
<br />
Por default Jetty no hace un reload del classpath cada vez que algo cambia -principalmente por que estar viendo el filesystem por cambios es una operación costosa.<br />
<br />
Al principio tener que reiniciar microwiki para ver los cambios en un template no me molestaba: el proyecto es chico y reiniciar es rápido.<br />
<br />
Sin embargo cuando empece a mejorar un poco el diseño del HTML esos segundos de presionar restart en el IDE y volver a cargar la página se volvieron una molestia.<br />
<br />
Por suerte para no meterme con cuestiones de classpath reload y configuración de Jetty, algunos astros de "mantenibilidad" y "testeabilidad" se alinearon.<br />
<br />
Para permitir que microwiki sea configurable mediante archivos de configuración, y que la interpretación de estos me sea fácil de mantener utilice un DSL de Groovy.<br />
Es decir los archivos de configuración son código fuente Groovy, y en ellos es posible definir un template de la siguiente manera:<br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;">templates {</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> display = '/dir/disp.html'</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> edit = '/other/edit.html'</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">}</span><br />
<br />
Por otro lado para facilitar el testing los templates implementan la interfaz: <a href="https://github.com/dfernandez79/microwiki/blob/master/src/main/groovy/microwiki/servlets/view/ViewTemplate.groovy">ViewTemplate</a>. Esto me facilita la creación de mocks en los tests.<br />
<br />
Juntando estas dos cosas, el cambio para facilitar el "developability" fue muy sencillo:<br />
<br />
<b>A</b>gregue un método al builder que interpreta el DSL del archivo de configuración (<a href="https://github.com/dfernandez79/microwiki/blob/master/src/main/groovy/microwiki/config/dsl/ConfigBuilder.groovy">ConfigBuilder</a>):<br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;">ViewTemplate debug(source, Map context = [:]) {</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> { Map ctx -> template(source, context).applyWith(ctx) } as ViewTemplate</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">}</span><br />
<br />
Con este método es posible definir la configuración de esta manera:<br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;">templates {</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> File prjDir = new File('/Users/diegof/Projects/microwiki/src/main/resources')</span><br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;"> edit = debug(new File(prjDir, 'microwiki/templates/edit.html'))</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> search = debug(new File(prjDir, 'microwiki/templates/search.html'))</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">}</span><br />
<div>
<br /></div>
<div>
Y ya no hay que preocuparse por el reload: microwiki va a leer el archivo cada vez, y los cambios se ven de forma instantánea (claro que esto no tiene en cuenta los cambios en el stylesheet, aunque es un avance).</div>
<div>
<br /></div>
<div>
De que sirve esto... cuando tengan uno de esos proyectos web que cargan millones de cosas al inicio, es bueno dedicar un poco de tiempo a pensar como mejorar el "developability": aunque los appserver pueden hacer reload de ciertas cosas, no pueden hacer magia, y a veces pensar un poco en esta característica como parte de la calidad de la solución les va a ahorrar horas de frustración.</div>Diego Fernándezhttp://www.blogger.com/profile/11282484801526539091noreply@blogger.com0tag:blogger.com,1999:blog-1951692453295043766.post-72782267454878598922011-12-13T17:20:00.000-03:002011-12-13T17:20:15.529-03:00Configuración basada en Java con Spring 3.xSi vienen siguiendo mi blog sabrán que además de postear una vez cada mil años, escribi algunas cosas sobre la <a href="http://diegacho.blogspot.com/2011/05/using-spring-dependency-injection.html">dificultad de generar una configuración de Spring usando solo Java</a>.<br />
<br />
La buena noticia es que a partir de Spring 3.x se puede configurar el application context casi 100% en Java (hay una pequeña parte que todavia hay que hacer con el XML). Me entere de esto leyendo el <a href="http://www.infoq.com/news/2011/12/spring31">anuncio del release 3.1 en InfoQ</a> (y para mi sorpresa esta posibilidad ya existía en el release 3.0).<br />
<br />
Si planean usar Spring 3.x es una buena noticia por que pueden tirar ese <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">applicationContext.xml</span> al tacho y <a href="http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/new-in-3.0.html#new-java-configuration">hacerlo directamente en Java</a> que es más fácil de mantener y refactorizar :)<br />
<br />
La implementación se basa mucho en annotations, pero es lo suficientemente flexible como para permitir refactorings (para ejemplos ver <a href="http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/beans.html#beans-java">la sección 3.11.3</a> del manual de Spring 3.0). Aunque hubiese preferido que lo hagan como en <a href="http://grails.org/">Grails</a>, donde hicieron un <a href="http://grails.org/doc/latest/guide/14.%20Grails%20and%20Spring.html#14.3 Runtime Spring with the Beans DSL">builder usando Groovy</a> (que dicho sea de paso, si están en un proyecto Java/Groovy pueden usar el builder sin necesidad de depender de otras librerías de Grails).<br />
<br />Diego Fernándezhttp://www.blogger.com/profile/11282484801526539091noreply@blogger.com0tag:blogger.com,1999:blog-1951692453295043766.post-7615224685634018472011-12-09T17:09:00.001-03:002011-12-12T11:28:19.798-03:00Mapeos Objeto/RelacionalLlegué a la conclusión de que los frameworks de mapeo Objeto/Relacional no sirven.<br />
<div>
<br /></div>
<div>
<i>- Ehh! ¿Por que? Si yo uso <a href="http://www.hibernate.org/">Hibernate</a> y esta bueno!</i></div>
<div>
<br /></div>
<div>
Aca van mis razones...</div>
<div>
<br /></div>
<div>
<span class="Apple-style-span" style="font-size: large;"><b>De Objetos...</b></span></div>
<div>
Aprendí con el tiempo que la principal ventaja de la programación orientada a objetos es la posibilidad de tratar con distintas implementaciones a traves de su interfaz. </div>
<div>
<br /></div>
<div>
Si estas pensando que me refiero al polimorfismo, tenes razón, pero di esta vuelta para evitar la tipica asociación de polimorfismo con jerarquias de clases.</div>
<div>
<br /></div>
<div>
Veamos un ejemplo que suele darse en los cursos introductorios: la cuenta bancaria.<br />
<br /></div>
<div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKVGRh3RPAGL5jljohC2xaZv4VzJkLtrV21N1GN5ou2PTu_JfUfP93EQlB-hn6-BBbtoBhQxfkGcLHpLgHfOuRdEJADXihDdr8sB4ldqQ7TrYBZkHF3l9KSp7_MoaVjB7tMBwerpVg7IZH/s1600/cuentabca.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKVGRh3RPAGL5jljohC2xaZv4VzJkLtrV21N1GN5ou2PTu_JfUfP93EQlB-hn6-BBbtoBhQxfkGcLHpLgHfOuRdEJADXihDdr8sB4ldqQ7TrYBZkHF3l9KSp7_MoaVjB7tMBwerpVg7IZH/s1600/cuentabca.png" /></a></div>
<br /></div>
<div>
Supongamos que tenemos en nuestro sistema un <i>Cajero</i> que puede hacer depositos en estas cuentas:<br />
<br /></div>
<div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgRVNZNzxB26Bm2tMQpkQa-sGiZciAaj5EUp4GzqtKxemHicd7gc00BokLMaw7HqG21Gqxh453VdYtnhogfHYAQb1eVTLSphG2ZxcTN3PFShHkSz7C36y_hi9r7xrtAXsUhfhgOKuD_sedc/s1600/cajero.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgRVNZNzxB26Bm2tMQpkQa-sGiZciAaj5EUp4GzqtKxemHicd7gc00BokLMaw7HqG21Gqxh453VdYtnhogfHYAQb1eVTLSphG2ZxcTN3PFShHkSz7C36y_hi9r7xrtAXsUhfhgOKuD_sedc/s1600/cajero.png" /></a></div>
<br /></div>
<div>
<br /></div>
<div>
La instancia <i>cuenta</i> se pudo haber definido mediante una sub-clase, clase anonima, proxy dinamico o usando AspectJ y manipulación de bytecode. No importa. Mientras cumpla con la interfaz <i>CuentaBancaria</i> nuestro <i>Cajero</i> va a funcionar.</div>
<div>
<br /></div>
<div>
<span class="Apple-style-span" style="font-size: large;"><b>... y Relacional</b></span></div>
<div>
Ahora supongamos que definimos una <i>Cuenta</i> de la siguiente manera:</div>
<div>
<br /></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">CuentaBancaria cuenta = new Decorator(cuentaOriginal);</span></div>
<div>
<br /></div>
<div>
Nuestro <i>Cajero</i> sigue funcionando pero...</div>
<div>
<br /></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">motorDeMapeoOR.guardar(cuenta);</span></div>
<div>
<br /></div>
<div>
explota.</div>
<div>
<br /></div>
<div>
El motor de mapeo O/R necesita saber como tratar la instancia para poder mapear su clase y sus variables de instancia a la base de datos. En otras palabras: encapsulamiento y polimorfismo las pelotas.</div>
<div>
<br /></div>
<div>
Quizas pienses que me fui a un ejemplo extremo y que uno nunca tiene casos como estos. </div>
<div>
<br /></div>
<div>
Sin embargo en todos los sistemas JEE que veo siempre hay un "modelo" que termina estando sumamente acoplado a la base de datos:</div>
<div>
<ul>
<li>No es conveniente jugar con las jerarquias de clases por que no se pueden mapear de una forma buena.</li>
<li>No se puede refactorizar algo para cambiar su composición por que es engorroso modificar los mapeos.</li>
<li>No se pueden usar objetos más "dinamicos", como objetos que tengan <i>closures</i> (en un lenguaje que lo permita), implemetaciones de interfaces definidas inline o simplemente un <a href="http://en.wikipedia.org/wiki/Decorator_pattern">decorator</a> como el del ejemplo.</li>
</ul>
</div>
<div>
Entonces la solución es: hacemos objetos pobres casi sin comportamiento solo para transferir datos (<a href="http://en.wikipedia.org/wiki/Data_transfer_object">DTO</a>s). Luego tenemos un modelo mas "rico" que se crea a partir de estos DTOs. </div>
<div>
<br /></div>
<div>
Suena simple, pero en la práctica termina siendo una engorrosa y aburridisima repetición de código que los programadores tienden a solucionar de forma sencilla: en lugar de separar estos DTOs del modelo de dominio, terminan juntandolo en un modelo de dominio que solo guarda datos y muchas "clases utilitarias". Es decir aprovechan muy poco las posibilidades de polimorfimo que brinda el lenguaje. </div>
<div>
<br /></div>
<div>
Pero si iba a estar todo tan acoplado y dependiente de la base de datos, ¿Para que nos preocupamos en hacer el mapeo? ¿No era mejor usar algo tipo <a href="http://martinfowler.com/eaaCatalog/activeRecord.html">Active Record</a>? Al fin de cuentas son lo mismo que los DTOs pero con menos código.</div>
<div>
<br /></div>
<div>
Esto es lo que hace <a href="http://rubyonrails.org/">Rails</a> y la primera vez que lo vi me parecio una cagada. Huele mal, pero los mapeos O/R no huelen mucho mejor.</div>
<div>
<br /></div>
<div>
<span class="Apple-style-span" style="font-size: large;"><b>...a las conclusiones</b></span></div>
<div>
Los frameworks de mapeo O/R no cumplen con su promesa de un modelo de dominio libre de la base de datos, y me pregunto cuales son las ventajas en usarlos. Es decir si los usamos solo para facilitar la interacción con JDBC, quizás hay otras maneras que no requieran ningún tipo de mapeo y mantenimiento de "beans" intermedios.</div>
<div>
<br /></div>
<div>
En el caso de Java, creo que si Sun en lugar de definir una interfaz tan fea como la de <a href="http://docs.oracle.com/javase/tutorial/jdbc/basics/rowset.html">RowSet</a> que mezcla un montón de responsabilidades, hubiese hecho algo mas sencillo que se pueda usar como un Active Record, no habria razones para utilizar mapeos O/R como <a href="http://en.wikipedia.org/wiki/Java_Persistence_API">JPA</a>.</div>Diego Fernándezhttp://www.blogger.com/profile/11282484801526539091noreply@blogger.com0tag:blogger.com,1999:blog-1951692453295043766.post-24667690925943903642011-11-09T11:50:00.000-03:002011-11-09T11:50:56.903-03:00Microwiki: Primeros pasos de diseñoNo 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.<br />
<br />
El secreto es que incluso con más experiencia, uno tambien se encuentra algo perdido cuando se empieza con un problema nuevo.<br />
<br />
Creo que un buen consejo para encarar un diseño es tener una "mente de principiante" y no dejar de preguntarse "¿Por qué?".<br />
Claro que para hacerse las preguntas y poder responderlas se necesita un conocimiento previo.<br />
Ambas cosas -hacerse las preguntas y construir ese conocimiento- van de la mano.<br />
<br />
Esta introducción, viene a cuento que me dieron ganas de contarles las preguntas que me fui haciendo en la construcción de microwiki.<br />
<br />
Para quienes no hayan visto <a href="http://diegacho.blogspot.com/2011/10/microwiki.html">mi post anterior</a>, microwiki, es un pequeño servidor <a href="http://en.wikipedia.org/wiki/Wiki">wiki</a> que comencé a programar en mis ratos libres. Como objetivos de diseño, microwiki tiene las siguientes características:<br />
<ul>
<li>Se utiliza localmente: no hay usuarios, ni permisos, ni historial de versiones en las paginas. </li>
<li>Iniciar el servidor debe ser tan simple como ejecutar un comando.</li>
<li>Las paginas se guardan en el file system, y pueden editarse tanto dentro como fuera de la aplicación web.</li>
<li>La búsqueda de contenido debe ser rápida.</li>
</ul>
<div>
<b><br /></b><br />
<b>Las cuestiones técnicas</b></div>
<div>
<br />
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 <a href="http://www.gradle.org/">Gradle</a> así que esas fueron las herramientas que elegí para trabajar.<br />
<br />
Al principio pensé en hacerlo con <a href="http://www.scala-lang.org/">Scala</a> para practicar un poco este lenguaje, pero la comodidad de <a href="http://www.jetbrains.com/idea/">IntelliJ IDEA</a> para usar <a href="http://groovy.codehaus.org/">Groovy</a> me compro -me estoy volviendo viejo, ya no tengo ganas de ponerme a configurar <a href="http://www.scala-ide.org/">plugins en versión beta</a>.<br />
<br />
El resto de las opciones fueron más simples. Conocía la sintaxis <a href="http://en.wikipedia.org/wiki/Markdown">Markdown</a> de usar <a href="https://github.com/">GitHub</a> y <a href="http://stackoverflow.com/">Stackoverflow</a>, y <a href="https://github.com/sirthias/pegdown">PegDown</a> fue el primer parser que encontré para Java.<br />
<br />
Y si voy a hacer un pequeño web server tampoco iba a empezar de cero, <a href="http://www.eclipse.org/jetty/">Jetty</a> 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 <a href="http://grizzly.java.net/">Grizzly</a>, pero es mucho más nuevo y no tiene tanta documentación).<br />
<br />
<br />
<b>Primeros pasos</b><br />
<br />
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.<br />
<blockquote class="tr_bq">
<b>Consejo:</b> Empezar a diseñar siempre por un caso particular y simple.</blockquote>
Entonces tenemos nuestro servlet que muestra la pagina. <b>¿Implementamos en el servlet la funcionalidad de abrir el archivo e invocar al parser?</b> Respuesta rápida: <b>no</b>.<br />
<b><br /></b><br />
<b>¿Por qué?</b> 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.<br />
<br />
Probablemente en alguna clase sobre diseño orientado a objetos escuchaste que las clases deberían tener <a href="http://en.wikipedia.org/wiki/Cohesion_(computer_science)">alta cohesión</a>, 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.<br />
<br />
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 <a href="http://download.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html">HttpServletRequest</a> y un mock <a href="http://download.oracle.com/javaee/6/api/javax/servlet/http/HttpServletResponse.html">HttpServletResponse</a>.<br />
<blockquote class="tr_bq">
<b>Consejo</b>: Los mock objects son útiles, pero si tus tests necesitan muchos, probablemente le estés pifiando en la separación de responsabilidades.</blockquote>
Entonces separando responsabilidades termine con algo así:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixb2xOFCYcUlUBhP4jIZ3LL4AuGwXaaMUCpnA7IKGETKB19A46fgouSZ7DlUYb1Q7lCBF7xDFhYCUtOY2-vv39K_B4-KAX_-xOImMG6mh7tw7QztSQyLtpuf18AQCUYy-jExvdzyGLCUXS/s1600/microwiki.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixb2xOFCYcUlUBhP4jIZ3LL4AuGwXaaMUCpnA7IKGETKB19A46fgouSZ7DlUYb1Q7lCBF7xDFhYCUtOY2-vv39K_B4-KAX_-xOImMG6mh7tw7QztSQyLtpuf18AQCUYy-jExvdzyGLCUXS/s1600/microwiki.png" /></a></div>
<br />
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 <a href="http://groovy.codehaus.org/Groovy+Templates">GStrings de Groovy</a>. Me pareció bueno mantener las cosas bien concretas: el template por ahora se utiliza para visualizar una pagina.<br />
<blockquote class="tr_bq">
<b>Nota:</b> 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.</blockquote>
<b>¿Por que Writable?</b><br />
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.<br />
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.<br />
<br />
<b>¿Por que un objeto Page y no retornar directamente el String con el contenido?</b><br />
Esta claro que para mi sistema una pagina no es simplemente un String.<br />
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.<br />
<br />
<b>¿Por qué una interface PageProvider?</b><br />
Bueno yo tambien tengo la misma duda :)<br />
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:<br />
<ul>
<li>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).</li>
<li>Si quisiera agregar un cache podría usar un <a href="http://en.wikipedia.org/wiki/Decorator_pattern">decorator</a> que implemente esta interfaz. <br />Otra vez me estoy adelantando -son las manias que uno adquiere de la experiencia previa- en estos casos <b>es importante tomar nota mental de que uno se esta adelantando</b>. <b>A veces por adelantarse, uno le puede errar fiero</b> (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.</li>
</ul>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8IogIgOcC7gb9O8y33I2uoMtfL0GL2B4acYztoqKaM5KaT8FxYdJgGkwYmTaTe-7TLlTX6ErNOZ3WdIXI8T1b0TgX512EjWV6FIpFX94Met0MUBDsMRflp5XOjqOdlNf4oI5kErBXXUpW/s1600/DT-CENTRAL-MOSTAZA-MERLO_CLAIMA20100828_0101_16.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8IogIgOcC7gb9O8y33I2uoMtfL0GL2B4acYztoqKaM5KaT8FxYdJgGkwYmTaTe-7TLlTX6ErNOZ3WdIXI8T1b0TgX512EjWV6FIpFX94Met0MUBDsMRflp5XOjqOdlNf4oI5kErBXXUpW/s320/DT-CENTRAL-MOSTAZA-MERLO_CLAIMA20100828_0101_16.jpg" width="320" /></a></div>
<br />
Espero no haberlos aburrido mucho, la próxima les cuento algunos pasos más.</div>Diego Fernándezhttp://www.blogger.com/profile/11282484801526539091noreply@blogger.com0tag:blogger.com,1999:blog-1951692453295043766.post-7674372309612581492011-10-17T19:04:00.000-03:002011-10-17T20:09:12.064-03:00MicrowikiEl feriado lluvioso de la semana pasada, lo dedique a un pequeño proyecto que tenia en mente hace tiempo: hacer una pequeña wiki.<br />
<div>
<br /></div>
<div>
¿Para que sirve? Sirve para tener un wiki sin necesidad de un servidor externo, guardando los archivos en forma de texto plano. La ventaja es que la documentación del proyecto se mantiene en una carpeta junto con el código, en una forma fácil de editar y buscar. El formato que usa es <a href="http://en.wikipedia.org/wiki/Markdown">Markdown</a>, que es lo suficientemente amigable y legible para mantener los archivos de texto sin necesidad del wiki.</div>
<div>
<br /></div>
<div>
Pueden encontrar el código en GitHub: <a href="https://github.com/dfernandez79/microwiki">https://github.com/dfernandez79/microwiki</a></div>
<div>
<br /></div>
<div>
Todavía esta en desarrollo, pero la funcionalidad básica de ver y editar esta disponible. Hay muchas cosas por hacer que voy a ir agregando cuando tenga tiempo, al menos la siguientes características van a estar terminadas para el "release 1.0":</div>
<div>
<ul>
<li>Búsqueda de texto rápida (quizás usando Lucene). Mi objetivo es que browsear y buscar sea rápido, de lo contrario no le veo mucha ventaja sobre utilizar archivos Word u HTML :)</li>
<li>Paquete binario fácil de usar, la idea es que el típico caso de uso sea hacer checkout de los archivos fuentes e iniciar el wiki (ya sea por un comando o un icono) con cero configuración (por default voy a hacer que tome el subdirectorio docs del directorio actual).</li>
<li>Modo solo lectura (lo veo útil para integrar con Hudson u algún otro servidor de CI)</li>
</ul>
<div>
Otras cosas que me gustaría agregar a futuro son:</div>
<div>
<ul>
<li>Soporte para LaTeX probablemente usando <a href="http://www.mathjax.org/">http://www.mathjax.org/</a>.</li>
<li>Soporte para renderizar grafos de GraphViz directamente (la idea seria que los links a gráficos usando la notación ![Alt text](file.dot) de Markdown se muestren directamente como PNG).</li>
</ul>
</div>
<div>
</div>
<div>
Si tienen interés en chusmear el código fuente, las herramientas que use son:</div>
</div>
<div>
<ul>
<li><a href="http://groovy.codehaus.org/">Groovy</a></li>
<li><a href="http://www.gradle.org/">Gradle</a> (una buena alternativa a Maven)</li>
<li><a href="http://code.google.com/p/spock/">SpockFramework</a> para los tests (les recomiendo mucho este framework, incluso para proyectos Java)</li>
<li><a href="http://www.eclipse.org/jetty/">Jetty</a></li>
<li><a href="https://github.com/sirthias/pegdown">PegDownParser</a> para parsear el texto en formato Markdown</li>
</ul>
</div>Diego Fernándezhttp://www.blogger.com/profile/11282484801526539091noreply@blogger.com2tag:blogger.com,1999:blog-1951692453295043766.post-11286981272022324512011-10-12T23:29:00.000-03:002011-10-12T23:30:22.229-03:00Un brevísimo vistazo a DartEl lunes pasado - 10/10/2011- Google <a href="http://gotocon.com/aarhus-2011/presentation/Opening%20Keynote:%20Dart,%20a%20new%20programming%20language%20for%20structured%20web%20programming">presentó</a> un nuevo lenguaje de programación llamado <a href="http://www.dartlang.org/">Dart</a>.<br />
<br />
La presentación realizada por <a href="http://bracha.org/Site/Home.html">Gilad Bracha</a> y <a href="http://en.wikipedia.org/wiki/Lars_Bak_(computer_programmer)">Lars Bak</a> había sido anunciada hace unos meses atrás.<br />
<br />
Dado que estos dos personajes hicieron cosas interesantes y tienen un <a href="http://www.strongtalk.org/">pasado común en Smalltalk</a>, el anuncio me genero mucha expectativa.<br />
<br />
Después de leer un poco el sitio (por ahora solo el código fuente esta disponible para el publico general), mis expectativas decayeron: al menos en la superficie Dart no es muy distinto a lo que vienen produciendo otros lenguajes como Scala o Groovy.<br />
<br />
Debido al trabajo de Gilad Bracha en <a href="http://newspeaklanguage.org/">Newspeak</a>, y a que JavaScript es un lenguaje basado en prototipos, yo esperaba un lenguaje estilo Newspeak pero con sintaxis ala JavaScript. (En <a href="http://youtu.be/24LIzl7cM2c">este video</a> G. Bracha habla justamente que Dart es un lenguaje más "conservador" en comparación con Newspeak)<br />
<br />
Tampoco voy a ser tan negativo, el gran punto a favor que tiene Dart es que si Google decide promocionarlo puede hacerlo a través the Chrome y Android. Y si logra que Mozilla lo adopte puede lograr algo similar a lo que ocurrió con HTML5: que IE tambien lo adopte como standard.<br />
<br />
Lo más interesante es que si las herramientas que provee el lenguaje son las adecuadas e incursionan en herramientas del lado servidor, pueden lograr un lenguaje de programación ubicuo. Hoy en día JavaScript con <a href="http://nodejs.org/">node.js</a> lo es, aunque las limitaciones del lenguaje respecto del manejo de módulos y la disponibilidad de herramientas no lo hacen para mi una opción fuerte del lado servidor.<br />
<br />
Según lo que pude leer del material disponible, Dart es todavía un "work in-progress", y no se definieron ciertas cosas importantes sobre como va a ser el manejo de modularidad. En resumen algunas características de este lenguaje son:<br />
<ul>
<li>Se va a poder <a href="http://www.dartlang.org/articles/embedding-in-html/">utilizar en el browser</a>, directamente si es Chrome (al menos en el futuro), o compilando JavaScript si no (como <a href="http://jashkenas.github.com/coffee-script/">CoffeScript</a>).</li>
<li>Es un lenguaje con clasificación con <a href="http://www.dartlang.org/articles/optional-types/">tipado opcional</a> (como <a href="http://www.strongtalk.org/index.html">Strongtalk</a> o <a href="http://groovy.codehaus.org/">Groovy</a>).</li>
<li>Al igual que en muchos lenguajes dinámicos, se pueden re-definir operadores y tiene algo de <i>syntax sugar</i> como strings con variables del estilo <i>"Hola $mundo"</i></li>
<li>Tiene generics y están reificados (los generics pueden ser opcionales como en Java y la información se mantiene "en runtime" como en <a href="http://msdn.microsoft.com/en-us/library/512aeb7t(v=vs.80).aspx">C#</a>).</li>
</ul>
Hasta ahora nada mal pero tampoco demasiado novedoso, lo que si destaco es:<br />
<ul>
<li>Tiene un modelo de actores (como <a href="http://www.erlang.org/">Erlang</a>), llamado Isolates. Me parece una forma buena de encarar concurrencia.</li>
<li>Un programa compilado se puede guardar en un "snapshot" que es como una imagen de Smalltalk (de hecho esta idea viene de Strongtalk, donde creo tambien se llama snapshot). Eso es interesante por que si logran agregar el soporte adecuado en los browers, quizás logren hacer lo que Java no pudo lograr: un lenguaje con bytecode multiplataforma que funcione directamente en todos los browsers.</li>
</ul>
<br />
Lo que no me gusto de lo que leí hasta ahora es:<br />
<ul>
<li>No vi absolutamente nada relacionado al manejo de "módulos" (de hecho ni siquiera existe el concepto de namespace o paquete), eso puede ser bueno: Gilad Bracha escribió <a href="http://gbracha.blogspot.com/search/label/Modularity">artículos</a> muy interesantes sobre este tema en su blog, asi que puede significar que todavía están evaluando la mejor forma de hacerlo. O bien puede ser malo, en <a href="https://code.google.com/p/dart/source/browse/branches/bleeding_edge/dart/samples/chat/chat_server.dart">los ejemplos</a> vi unos <i>#import </i> que funcionan como un include C que huelen muy mal.</li>
<li>Factories e interfaces. No sé si poner esto entre las cosas buenas o malas. Se me ocurren varios casos donde puede ser bueno: refactoring de una clase concreta a una interfaz, usos de implementaciones comunes por default (ej. en Java: HashSet o ArrayList). Sin embargo tienen un lado malo: funciona como un mecanismo rudimentario para manejo de dependencias estilo "service locator", algo que en Java demostró ser muy problemático.</li>
<li>Uso de underscore para indicar que algo es privado (<a href="http://www.dartlang.org/docs/spec/dartLangSpec.pdf">Lang Spec</a> sección 3.2). Es un shortcut aceptable para las variables de instancia, pero para los métodos me parece demasiado que quienes lo invocan sepan si lo que usan es privado o publico.</li>
<li>Las librerías "core" son una copia de las de Java, hubiese estado bueno que cambien algunas cosas como por ejemplo tener listas mutables e inmutables (ala Scala).</li>
<li>No vi que los generics se puedan acotar, por ejemplo <a href="http://www.dartlang.org/docs/api/HashSet.html#HashSet::HashSet">HashSet<E></a> no es un HashSet<E extends Hashable> (Object en Dart no implementa hashCode, hay una interfaz Hashable).</li>
</ul>
En fin veremos que depara el futuro, es difícil hacer predicciones en estas cosas, pero creo que no es necesario que el lenguaje sea bueno para que tenga éxito si no que la plataforma lo sea. Si Google decide usar Dart para hacer aplicaciones Android nativas, y programar en el web browser, entonces puede que se convierta en el sucesor de Java. Sin embargo no creo que eso pase, desde afuera parece que la estrategia de Google es apostar a muchas cosas para ver que queda.Diego Fernándezhttp://www.blogger.com/profile/11282484801526539091noreply@blogger.com2tag:blogger.com,1999:blog-1951692453295043766.post-56012169310449928312011-09-27T12:03:00.000-03:002011-09-27T12:03:07.375-03:00Tip rápido para los que usan MacSi usan MacOSX y tienen <a href="http://growl.info/developer/">Growl</a>, les recomiendo instalar el extra de <a href="http://growl.info/extras.php#growlnotify">growlnotify</a> (esta en la carpeta Extras de la <a href="http://growl.cachefly.net/Growl-1.2.2.dmg">imagen de instalación de Growl</a>).<br />
<br />
Es muy util para notificar cuando termina una tarea de esas que llevan tiempo como un build o un update, por ejemplo:<br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">svn update && growlnotify -m "Termino el update"</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">mvn clean install && growlnotify -m "Termino el build"</span>Diego Fernándezhttp://www.blogger.com/profile/11282484801526539091noreply@blogger.com0tag:blogger.com,1999:blog-1951692453295043766.post-14306610816651789782011-09-14T21:12:00.000-03:002011-09-14T21:12:53.102-03:00Herencia en JavaScriptJavaScript es en esencia un lenguaje basado en prototipos, que sufre de <a href="http://es.wikipedia.org/wiki/Me,_Myself_%26_Irene">personalidades múltiples</a>: tiene algunos operadores y comportamientos de un lenguaje con clasificación.<br />
<br />
Para entender esto olvidemos por un momento a JavaScript.<br />
<br />
Un lenguaje orientado a prototipos -como por ejemplo <a href="http://en.wikipedia.org/wiki/Self_(programming_language)">Self</a>- se basa en que uno obtiene nuevas instancias a partir de la clonación de otras, por ejemplo en un "pseudo-JavaScript":<br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><b>var</b> auto = {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <i>acelerarACienEnUnSegundo</i>: <b>function</b>() { <b>return</b> 'No puedo'; },</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <i>arrancar</i>: <b>function</b>() { <b>return</b> 'brummm... :)' }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">};</span><br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><b>var</b> ferrari = <i>clonar</i>(auto);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">ferrari.<i>acelerarACienEnUnSegundo</i> = <b>function</b>() { <b>return</b> 'fiuuummm'; };</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">ferrari.<i>arrancar</i>(); <span class="Apple-style-span" style="color: #666666;">// retorna brummm....</span></span><br />
<br />
¿Donde esta la esquizofrenia de JavaScript? Esta en que contamos con un operador <b>new</b> que simula comportarse como si estuviésemos trabajando con un lenguaje con clasificación, por ejemplo:<br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><b>var</b> auto = <b>new</b></span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> Auto();</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><b>function</b> Auto() {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> this.arrancar = <b>function</b>() { <b>return</b> 'brummm....'; };</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">}</span><br />
<br />
¿Qué hace <b>new</b>?<br />
Cada función que uno define en JavaScript es un objeto que tiene asociada una propiedad <i>prototype</i>.<br />
Es como si cuando uno escribe <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">function Auto() {}</span> escribiera:<br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><b>var</b> Auto = {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <i>prototype</i>: {}, </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <i>constructor</i>: <b>function</b>() { /* cuerpo de la función Auto */ } };</span><br />
<br />
Cuando se usa el operador <b>new</b> JavaScript lo que hace es:<br />
<br />
<ul>
<li>Clonar el objeto <i>prototype</i></li>
<li>Después evaluar la función en <i>constructor</i> usando la nueva instancia como contexto (es decir this es el clon de prototype)</li>
</ul>
<br />
Es por eso que uno podría escribir la función Auto como:<br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><b>function</b> Auto() { console.debug(this.arrancar); }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Auto.<i>prototype</i>.<i>arrancar</i> = <b>function</b>() { return 'brummm....'; };<span class="Apple-style-span" style="font-size: x-small;"> </span></span><br />
<br />
Esto es muy confuso, por que pareciera que una función se puede comportar como si fuese una clase. De hecho genera más confusión aún cuando uno quiere compartir implementación usando algo similar a la herencia.<br />
<br />
Sin embargo el problema es más simple de resolver: no hay clases ni tipos, solo tenemos que agregar los métodos que necesitamos compartir, por ejemplo:<br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><b>var</b> <i>agregarComportamientoDeAuto</i> = function(obj) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> obj.arrancar = <b>function</b>() { return 'brummm... :)' };</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">}</span><br />
<br />
¿Como creamos un objeto que se comporte como un auto?<br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><b>var</b> ferrari = <b>function</b>() {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <b>var</b> nuevaInstancia = {};</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <b>agregarComportamientoDeAuto(nuevaInstancia);</b></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> nuevaInstancia.<i>acelerarACienEnUnSegundo</i> = <b>function</b>() { <b>return</b> 'fiuuummm'; };</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">};</span><br />
<br />
Quizás no luzca muy elegante, pero en mi opinión es mejor que simular un lenguaje con clasificación donde no lo hay.<br />
La ventaja de este método es que es muy fácil crear mixins o tener variables privadas (en el ejemplo podríamos pasar argumentos adicionales a <i>agregarComportamientoDeAuto</i> y aprovechar las clausuras).<br />
<br />
También es posible simular delegación estilo "super", la próxima voy a dar un par de ejemplos de eso, pero adelanto que es una solución muy poco elegante.Diego Fernándezhttp://www.blogger.com/profile/11282484801526539091noreply@blogger.com0tag:blogger.com,1999:blog-1951692453295043766.post-10521898965260833892011-09-02T01:09:00.000-03:002011-09-02T01:09:42.729-03:00Some Java "dependency injection" bad practicesWhen Spring and other <a href="http://martinfowler.com/articles/injection.html">dependency injection</a> frameworks appeared on the Java space, some of its goals where to be "non-invasive" and to facilitate unit testing (see this old <a href="https://docs.google.com/viewer?a=v&q=cache:FmPGys3PUjQJ:www.websphereusergroup.org.uk/downloads/04june/Introduction%2520to%2520open-source%2520Spring%2520framework%2520for%2520J2EE.ppt+The+Spring+Framework:+Motivation+and+Concepts&hl=en&gl=ar&pid=bl&srcid=ADGEESjnThOXDFikIswLNEyMopjHTLtWZTGLAhBZx_QzHnH_GQ7OG4AC2331u9NEens4T6U1EG5gt0uIXF8CS9jg4xDTETABax_ZoOEk998lrh9koBvTuWLq_l394ge5WF2z6oE6fBXB&sig=AHIEtbRUOXwwC5ox73vtyYLB3ztc-x4T8Q&pli=1">Rod Johnson presentation</a>).<br />
<br />
But when DI frameworks started to take advantage of annotations introduced in Java 5, the term "non-invasive" became relative, why? Here are some reasons:<br />
<br />
<b><span class="Apple-style-span" style="font-family: inherit; font-size: large;">@Autowire and @Inject annotation in private fields/private constructors</span></b><br />
<br />
<a href="http://static.springsource.org/spring/docs/2.5.x/reference/beans.html#beans-annotation-config">Spring</a>, <a href="http://jcp.org/en/jsr/detail?id=330">CDI</a> and <a href="http://code.google.com/p/google-guice/">Guice</a> allows you to annotate private fields, to let the DI framework inject the appropriate instance. But this feature has two problematic consequences, lets see them in a example taken from the <a href="http://docs.jboss.org/weld/reference/latest/en-US/html/injection.html#d0e1113">Jboss CDI documentation</a>:<br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">public class Checkout { <br /> private @Inject ShoppingCart cart; <br />
}</span><br />
<div>
<br /></div>
<div>
Consequences:<br />
<br />
<ol>
<li>Is impossible to create a valid <i>Checkout</i> instance without using a framework that supports the <i>@Inject </i>annotation. </li>
<li>You would not see the dependency directly by looking into the <i>Checkout</i> public interface or JavaDocs.</li>
</ol>
</div>
<div>
(1) is bad because your code becomes more coupled with the DI framework. For example if you want to create an unit test that uses a <i>Checkout</i> instance, you have to set up the DI framework first.<br />
<br />
There are annotations and test classes to make tests for Spring beans, but in my experience (after having to maintain <i>application-context.xml</i> files created and used by multiple tests) is an unnecessary complexity that could be removed if the code is not coupled with the DI framework.<br />
<br />
Some people argue that (2) is good (for example: <a href="http://code.google.com/p/google-guice/wiki/KeepConstructorsHidden">this</a> Guice "best practice"). The idea behind that is that developers will do "bad things" if they can: instead of obtaining the required instance by using dependency injection, they will use the constructor.<br />
But that has more to do with a communication problem, and should be resolved by using code reviews, <a href="http://en.wikipedia.org/wiki/Pair_programming">pair programming</a> and <a href="http://en.wikipedia.org/wiki/Software_walkthrough">walkthroughs</a>.<br />
<br />
<span class="Apple-style-span" style="font-size: large;"><b>@Required</b></span><br />
In the same tone as the @Inject for private fields comes the @Required annotation supported by Spring. That annotation allows you to write things like:<br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">public class Shape {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> private Color color;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> @Required</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> public void setColor(Color c) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> color = c;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> ....</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">}</span><br />
<br />
Then the DI framework will give you an exception if the dependency is missing.<br />
<br />
But what happens if you instantiate a <i>Shape</i> outside the DI framework? You don't know if you have to set the color or not.<br />
<br />
A constructor is simple: no dependencies with the DI framework and comes free with the Java language :) (<a href="http://martinfowler.com/articles/injection.html#ConstructorVersusSetterInjection">This part</a> from the "famous" Martin Fowler article on DI, also talks about constructors and setters in DI frameworks)<br />
<br />
<blockquote>
<b>Note for the reader:</b> I wrote the last articles in English because I wanted to practice that language. But I think that is better to keep the blog posts in one language, and Spanish is my native language, that's why this will be my last post in English (at least in this blog).</blockquote>
<br />
<br />
<br /></div>
Diego Fernándezhttp://www.blogger.com/profile/11282484801526539091noreply@blogger.com0tag:blogger.com,1999:blog-1951692453295043766.post-21298492697260420112011-07-26T12:00:00.002-03:002011-07-26T22:11:30.760-03:00Using embedded databases in unit testsTwo years ago, I wrote a <a href="http://diegacho.blogspot.com/2009/12/test-driven-development-algunas.html">long blog post</a> -in Spanish- about <a href="http://en.wikipedia.org/wiki/Test-driven_development">Test Driven Development</a>. One of the sections of that blog post was about testing code that depends on a database. In short, <b>never use an external database for unit tests</b>.<br />
<br />
Using mock objects to simulate the database connection is impractical. A better option is to start and shutdown a embed database. For that matter pure Java SQL databases like <a href="http://hsqldb.org/">HSQL DB</a> are really lightweight, for example a unit test that reads resources from disk usually takes more time than one that uses HSQL in memory.<br />
<br />
At the time of that blog post I wrote a small code snippet to start and shutdown HSQL. I used that code snippet many times, but never took the time to create a reusable library for it. Fortunately the Spring guys made it for me :)<br />
<br />
Since Spring Jdbc 3 there <a href="http://static.springsource.org/spring/docs/3.0.5.RELEASE/reference/jdbc.html#jdbc-embedded-database-support">is an interface to handle embed databases for testing</a>.<br />
<br />
If you use Maven, add the dependency to Spring Jdbc (you don't have to use the whole Spring framework if you don't want to) and to HSQLDB:<br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><dependency> <br />
<groupId>org.springframework</groupId> <br />
<artifactId>spring-jdbc</artifactId> <br />
<version>3.0.5.RELEASE</version> <br />
<scope>test</scope> <br />
</dependency><br />
<dependency> <br />
<groupId>hsqldb</groupId></span><br />
<div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <artifactId>hsqldb</artifactId></span><br />
<div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <version>1.8.0.7</version></span><br />
<div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <scope>test</scope></span><br />
<div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></dependency></span><br />
<div><br />
Start the embed database before your tests:<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></span><br />
<div style="font-size: small;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"><br />
</span></span></div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">@BeforeClass <br />
public static void setUpDatabase() {<br />
sharedDatabase = new EmbeddedDatabaseBuilder() </span><br />
<div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> .addScript("hsqldb-test-ddl.sql")<br />
.build()<br />
}</span></div><div><br />
</div><div>The <i>EmbedDatabase</i> implements <i>DataSource</i>, so you can pass it to your objects:<br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">hibernateSessionFactory.openSession(sharedDatabase.getConnection());</span><br />
<br />
Shutdown the database after the tests:<br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">@AfterClass <br />
public static void tearDownDatabase() { <br />
if (sharedDatabase != null) { <br />
sharedDatabase.shutdown(); <br />
} <br />
}</span><br />
<br />
Usually starting the HSQL server is fast, so you can use <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">@Before</span> and <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">@After</span> (instead of <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">@BeforeClass</span> / <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">@AfterClass</span>).<br />
<br />
That's all.<br />
<br />
</div></div></div></div></div></div>Diego Fernándezhttp://www.blogger.com/profile/11282484801526539091noreply@blogger.com0tag:blogger.com,1999:blog-1951692453295043766.post-55098257003879125832011-05-02T19:49:00.001-03:002011-05-02T19:50:33.889-03:00Using Spring dependency injection without XMLThis is my second post in English, which is not my first language, so if you find errors let me know :)<br>Having said that, let’s go with the blog post… <p>It’s difficult to define what the <a href="http://www.springsource.org/about">Spring Framework</a> provides, because is not a framework but a collection of them. The included frameworks covers pretty much everything from <a href="http://martinfowler.com/articles/injection.html">dependency injection</a> (DI) to the creation of web applications. All of them designed to work seamlessly with the DI framework, and in particular with the XML way of configuring DI.</p> <p>By using dependency injection you don’t need to modify your model to use Spring: you depend only on the DI framework to get the root objects of your application. At “lower levels” you can use some abstractions provided by the Spring Framework like <a href="http://static.springsource.org/spring/docs/2.5.4/api/org/springframework/jdbc/core/JdbcTemplate.html">JdbcTemplate</a> or <a href="http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/jms/core/JmsTemplate.html">JmsTemplate</a>, but you can use them without the DI framework too: they are part of the “Spring framework collection”, but they can work as independent frameworks.</p> <p>Sadly in my experience, it seems that most of the developers are not aware of that: a lot of projects are too coupled with the Spring XMLs (which is only one convenient way of configuring DI), to the point that there are also dependencies to the “application-context.xml” in the unit tests. This is really bad. Spring XML is easy to write, but is a pain to maintain: it lacks abstraction mechanisms, you can’t debug it, is difficult to refactor and to find where the things are. </p> <p>An alternative is to use <a href="http://code.google.com/p/google-guice/">Google Guice</a> for the dependency injection part: It contains a nice API to define instance dependencies in code. You can use it with other Spring things, but I never feel comfortable to do that, because I think that mixing both frameworks could be a little bit confusing.</p> <p>Spring DI can be configured by code, but to do it you have to pass some barriers:</p> <ul> <li>All the documentation refers to the XML configuration. <li>The API to do the configuration directly in Java is awful.</li></ul> <h2> </h2> <h2>Understanding the Spring API</h2> <p>The base interface to get instances from the DI injection framework is <a href="http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/beans/factory/BeanFactory.html">BeanFactory</a>. The “Bean” prefix is there for historical reasons, but not be confused by it: your object classes doesn’t need to comply with the <a href="http://www.oracle.com/technetwork/java/javase/documentation/spec-136004.html">JavaBeans specification</a>.</p> <p>But to use a BeanFactory instance you need to configure it in some way, that’s why exists other interface called <a href="http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/beans/factory/support/BeanDefinitionRegistry.html">BeanDefinitionRegistry</a>. This interface defines methods to register bean definitions (instances of <a href="http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/beans/factory/config/BeanDefinition.html">BeanDefinition</a>) that indicates how to create an instance.</p> <p>To summarize:</p> <ul> <li>BeanFactory: provides bean instances by name (getBean) <li>BeanDefintionRegistry: associates bean names with bean definitions (<strong>registerBeanDefinition</strong>). <li>BeanDefinition: provides information on how to create an object instance.<br><br><strong>Design side note:</strong> To me Spring is unnecessary complex in this part. The BeanDefinition provides information but it doesn’t do the instantiation, it means that each BeanFactory has to do that work (see AbstractBeanFactory.doGetBean). I think that if Spring delegates the instantiation to the definition, a lot of things could be simplified and the API becomes easier to use directly from Java.</li></ul> <p>If you used Spring before, you probably heard the name “ApplicationContext”. This is because the usual ways to get an implementation of BeanFactory is by using a ClassPathXmlApplicationContext or a XmlWebApplicationContext. That classes also implements other interfaces (i.e. <a href="http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/context/ApplicationContext.html">ApplicationContext</a>) that allows to plug-in lifecycle observers or enumerate beans (I assume that those functionality is required by other more “advanced” features provided by the framework).</p> <p> </p> <h2>Creating a BeanFactory instance for testing purposes</h2> <p>Before explaining how to do that… I want to give you an advice:</p> <blockquote> <p>If you need to pass a Spring “ApplicationContext” or a “BeanFactory” in a lot of tests, you need to review your design and/or the way that you are testing. Maybe you are putting direct dependencies with the Spring context when all you need is to pass the required instances directly or maybe you are instantiating too much just to create an unit test.</p></blockquote> <p>After that short advice, here are some small code examples:</p> <p>You can define beans by code, sadly the API is really ugly: <a href="http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/beans/factory/support/BeanDefinitionBuilder.html">BeanDefinitionBuilder</a> provides you with methods to define beans (Ey, Spring guys! A good internal DSL to do this will be nice in the next release of Spring):</p> <p><font face="Courier New">final StaticApplicationContext context = new StaticApplicationContext();<br>final BeanDefinitionBuilder builder = <br> </font><font face="Courier New">BeanDefinitionBuilder.genericBeanDefinition(MockDataSource.class);<br>context.registerBeanDefinition("dataSource", builder.getBeanDefinition());</font></p> <p><font face="Courier New">new MyApplication(context).run(); <font color="#008000"><strong>// MyApplication(BeanFactory context)</strong></font></font></p> <p>Using <a href="http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/beans/factory/support/StaticListableBeanFactory.html">StaticListableBeanFactory</a> you can pass instances, instead of using bean definitions (really useful to do tests): </p> <p><font face="Courier New">final StaticListableBeanFactory context = new StaticListableBeanFactory();<br>context.addBean("dataSource", new MockDataSource());</font></p> <p><font face="Courier New">new MyApplication(context).run();</font></p> <p>By using a <a href="http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/context/support/GenericApplicationContext.html">GenericApplicationContext</a> you can combine XML from different sources with beans defined in code, for example:</p> <p><font face="Courier New">GenericApplicationContext context = new GenericApplicationContext();<br>XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);<br><font color="#008000"><strong>// this xml define the contentManager bean</strong><br></font>reader.loadBeanDefinitions(new ClassPathResource("spring-config.xml")); </font> <p><font face="Courier New">context.registerBeanDefinition("dataSource",<br> BeanDefinitionBuilder.genericBeanDefinition(MockDataSource.class)<br> .getBeanDefinition());<br>context.refresh(); </font> <p><font face="Courier New"><font color="#008000"><strong>// contentManager uses dataSource</strong></font><br>System.out.println(context.getBean("contentManager"));</font></p> <p> </p> <p>Ok that’s all for this time :)</p> Diego Fernándezhttp://www.blogger.com/profile/11282484801526539091noreply@blogger.com0tag:blogger.com,1999:blog-1951692453295043766.post-13188955159907493922011-04-18T00:20:00.006-03:002011-04-18T11:29:45.815-03:00Java coding best practicesFor a software project in which I worked as a external code/design reviewer, a contractor asked me to do a document with Java coding best practices.<br />
<br />
Instead of giving them a big document that nobody is going to read, I preferred to make a small list of good practices - and try to fit them in a A4 page.<br />
<br />
I'm not a big fan of "best practices" documents. I know that they are good to standardize the work and share knowledge. But there is a "dark side": a "best practice" depends on a particular context. So is good to understand why a practice was good and distinguish when there are better options; in the end you can't get blood out of a stone: the best way to produce good quality systems is to work with good professionals.<br />
<br />
So this is the list of best practices that I made (in no particular order):<br />
<br />
<span class="Apple-style-span" style="font-size: large;">Choose Meaningful Names</span><br />
<blockquote>
<span class="Apple-style-span" style="font-size: x-small;">- Classes:</span><br />
<span class="Apple-style-span" style="font-size: x-small;"> If a class represents a concept of the problem domain, use a well known name for that domain (the technical analyst/product owner is our friend here).</span><br />
<span class="Apple-style-span" style="font-size: x-small;"> Forget the name, look at the class responsibilities and them choose a name based on that (<a href="http://c2.com/doc/oopsla89/paper.html">CRC helps</a>).</span><br />
<span class="Apple-style-span" style="font-size: x-small;"> Using suffixes like "Data", "Info", "Object"; doesn't add information and can be confusing ie. What is the difference between Customer and CustomerInfo?</span> </blockquote>
<blockquote>
<span class="Apple-style-span" style="font-size: x-small;"> </span><span class="Apple-style-span" style="font-size: x-small;">- If you still don't known how to name something and you invent a new name: communicate that to your team, and be sure that everybody agrees to name "the thing" in the same way.</span> </blockquote>
<blockquote>
<span class="Apple-style-span" style="font-size: x-small;"> </span><span class="Apple-style-span" style="font-size: x-small;">- When you start to feel uncomfortable with a method or class name and you have a better name, don't heistate: rename it.</span></blockquote>
<span class="Apple-style-span" style="font-size: large;">Write short methods</span><br />
<blockquote>
<span class="Apple-style-span" style="font-size: x-small;">- Remember the "Extract method" refactoring shortcut keys, you are going to use them a lot.</span> </blockquote>
<blockquote>
<span class="Apple-style-span" style="font-size: x-small;"></span><span class="Apple-style-span" style="font-size: x-small;">- Methods from comments: usualy in a long method programmers start to use comments to explain parts of the code, create a new method instead of using comments in that way.</span> </blockquote>
<blockquote>
<span class="Apple-style-span" style="font-size: x-small;"></span><span class="Apple-style-span" style="font-size: x-small;">- If you can't do an extract method because you end with lots of parameters or you have some obscure dependency; is a sign that something is wrong with the design. Sometimes using a "<a href="http://my.safaribooksonline.com/book/software-engineering-and-development/patterns/9780321413093/methods/ch08lev1sec4#X2ludGVybmFsX0ZsYXNoUmVhZGVyP3htbGlkPTk3ODAzMjE0MTMwOTMvODI=">Method Object</a>" to fix that helps to find what is missing.</span></blockquote>
<span class="Apple-style-span" style="font-size: large;">Avoid undesired side-effects (use immutable objects as much as possible)</span><br />
<blockquote>
<span class="Apple-style-span" style="font-size: x-small;">- Avoid mutable global state, ie: non-final static variables or "singleton" like globals (see "Avoid Singletons").</span> </blockquote>
<blockquote>
<span class="Apple-style-span" style="font-size: x-small;">- Try to use constructors with arguments instead of a combination of "default constructor/setters" (this is related with "Avoid object instances in invalid state" and "Avoid nulls")-</span> </blockquote>
<blockquote>
<span class="Apple-style-span" style="font-size: x-small;">- Be careful when returning collections: most of the Java collections are mutable, returning those could expose your object internal state.</span></blockquote>
<span class="Apple-style-span" style="font-size: large;">Avoid object instances in an "invalid state"</span><br />
<div>
<blockquote>
<span class="Apple-style-span" style="font-size: x-small;">- Declare required instance variables as final, and initialize them in the constructor.</span> </blockquote>
<blockquote>
- <span class="Apple-style-span" style="font-size: x-small;">Use good defaults.</span> </blockquote>
<blockquote>
<span class="Apple-style-span" style="font-size: x-small;">- I</span><span class="Apple-style-span" style="font-size: x-small;">f you end with a constructor with lots of parameters: refactor the code, maybe you can group of those parameters in an object.</span></blockquote>
<blockquote>
<span class="Apple-style-span" style="font-size: x-small;">- If you need to build a complex instance, you can <a href="http://en.wikipedia.org/wiki/Builder_pattern">create a builder</a> (but never put the validations in the builder, put them in the constructor).</span></blockquote>
<span class="Apple-style-span" style="font-size: large;">Avoid nulls</span><br />
<blockquote>
<span class="Apple-style-span" style="font-size: x-small;">- You can use the <a href="http://en.wikipedia.org/wiki/Null_Object_pattern">Null Object pattern</a></span> </blockquote>
<blockquote>
<span class="Apple-style-span" style="font-size: x-small;"><a href="http://en.wikipedia.org/wiki/Null_Object_pattern"></a>- Use good defaults instead of null</span></blockquote>
<span class="Apple-style-span" style="font-size: large;">Use singletons ONLY to represent unique objects</span><br />
<blockquote>
<span class="Apple-style-span" style="font-size: x-small;">- A lot of programmers use the <a href="http://en.wikipedia.org/wiki/Singleton_pattern">singleton pattern</a> to point to globally well known objects, this combined with mutable objects is a time bomb: lots of undesired side effects and code that could have concurrency problems.</span> </blockquote>
<blockquote>
<span class="Apple-style-span" style="font-size: x-small;">- If you need a "setInstance" method for testing purposes: is not a singleton!</span> </blockquote>
<blockquote>
<span class="Apple-style-span" style="font-size: x-small;">- Usually "Null Objects" can be singletons, but most of the time singletons are not needed.</span> </blockquote>
<blockquote>
<span class="Apple-style-span" style="font-size: x-small;">- Singletons should be immutable.</span></blockquote>
<span class="Apple-style-span" style="font-size: large;"><a href="http://www.objectmentor.com/resources/articles/srp.pdf">Classes should have one responsibility</a> - one reason to change</span><br />
<blockquote>
<span class="Apple-style-span" style="font-size: x-small;">- Using the <a href="http://c2.com/doc/oopsla89/paper.html">CRC method</a> sometimes helps to think in single responsibility classes.</span> </blockquote>
<blockquote>
<span class="Apple-style-span" style="font-size: x-small;">- Classes with many responsibilities tend to be difficult to test with unit tests; so a hard to write unit test could be a sign that you are dealing with too many responsibilities.</span></blockquote>
<span class="Apple-style-span" style="font-size: large;">Write unit tests like specifications</span><br />
<blockquote>
<span class="Apple-style-span" style="font-size: x-small;">- In <a href="http://en.wikipedia.org/wiki/Test-driven_development">TDD</a> the most confusing part of unit test is the word "test". If you think in tests like in small specifications of that your code supposes to do, then is very easy to write tests first.</span></blockquote>
<blockquote>
<span class="Apple-style-span" style="font-size: x-small;">- Don't put to many assertions in one test method, is better to have only one or two assertions per unit test method. Why? You'll start working faster with <a href="http://en.wikipedia.org/wiki/Test-driven_development#Test-driven_development_cycle">the TDD cycle</a> and if something fails is easy to see what's happening.</span></blockquote>
<span class="Apple-style-span" style="font-size: large;">Use exceptions instead of return codes</span><br />
<blockquote>
<span class="Apple-style-span" style="font-size: x-small;">- If you "Avoid Nulls" you will find this straight forward.</span></blockquote>
<blockquote>
<span class="Apple-style-span" style="font-size: x-small;">- Think in exceptions in a layered way, and handle them in the place that you could do something about it.</span></blockquote>
<blockquote>
<span class="Apple-style-span" style="font-size: x-small;">- Don't hide exceptions with empty catch blocks: if you have a bug it will be very difficult to find.</span></blockquote>
<blockquote>
<span class="Apple-style-span" style="font-size: x-small;">- Never catch Throwable: because you can catch sub-classes of Error too</span>.</blockquote>
<span class="Apple-style-span" style="font-size: large;">When creating polymorphic class hierarchies: remember the <a href="http://www.objectmentor.com/resources/articles/lsp.pdf">substitution principle</a></span><br />
<blockquote>
<span class="Apple-style-span" style="font-size: x-small;">- If you have a method that is overridden by different subclasses: avoid coupling between the type of the method argument and specific subclasses.</span></blockquote>
<blockquote>
<span class="Apple-style-span" style="font-size: x-small;">- If you need to cast an argument, maybe you are not following the substitution principle, take account that even if the "subtypes" are ok to the compiler you will not be able to change implementations easily.</span> </blockquote>
<span class="Apple-style-span" style="font-size: large;">Optimize only if it's necessary and with the help of a profiler</span><br />
<blockquote>
<span class="Apple-style-span" style="font-size: x-small;">- Applying optimization tips blindly... tend to create wrong assumptions, and sometimes code that is difficult to understand.</span></blockquote>
<blockquote>
<span class="Apple-style-span" style="font-size: x-small;">- A linear search in a collection with a bounded number of elements <a href="http://en.wikipedia.org/wiki/Computational_complexity_theory">is O(1)</a> (even when the max number of elements is 100, 1000 or 10000)</span></blockquote>
<blockquote>
<span class="Apple-style-span" style="font-size: x-small;">- I/O is orders of magnitude more expensive than CPU/Memory tasks.</span></blockquote>
<blockquote>
<span class="Apple-style-span" style="font-size: x-small;">- Generational Garbage Collectors like the ones in Java are optimized for short lived objects, it means that if you need to create objects to write more understandable code: create them. Early optimizations in object instance creation could cause other problems in concurrency and impact in the GC time; optimize object creation ONLY if you conclude that is necessary after using a profiler.</span></blockquote>
</div>Diego Fernándezhttp://www.blogger.com/profile/11282484801526539091noreply@blogger.com6tag:blogger.com,1999:blog-1951692453295043766.post-39596769921229792282011-04-09T11:57:00.001-03:002011-04-09T11:59:57.826-03:00Juego de la vida (2da parte: implementación)<br />
Hecha la introducción sobre el <a href="http://es.wikipedia.org/wiki/Juego_de_la_vida">juego de la vida de Conway</a> en el <a href="http://diegacho.blogspot.com/2011/03/juego-de-la-vida-1ra-parte.html">post anterior</a>, paso a contarles algunos detalles de implementación.<br />
No voy a referirme al algoritmo: es el primero que me salió, en cambio vamos a ver algunas cosas interesantes para aprender de JavaScript y HTML5.<br />
<br />
<br />
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<b>Doctype </b></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
La primer duda que me surgió a la hora de escribir el HTML5, es ¿Qué DOCTYPE uso?</div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
Quienes recién comienzan con HTML quizás no entiendan esta duda, es una larga historia resumida de forma excelente en "Dive into Html 5": <a href="http://diveintohtml5.org/semantics.html#the-doctype">http://diveintohtml5.org/semantics.html#the-doctype</a> .</div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<br /></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
En pocas palabras, el DOCTYPE que debe usarse en HTML5 es simplemente: <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><!DOCTYPE html ></span><br />
<br /></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<b><br /></b></div>
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<b>Progressive Enhancement</b></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZxuygIYsoFcVheqcozT3bKBC5u6IylDkqf_g_QllSAeZpL6jQAdmaX8hPtJZfKi_yjjM7ArnkDjugsOYu6jth9-godPXM_YHY2P1BsRh4GmBDNDYSPB-nuxbnE_c6rNKoxb5KMJnQ3gOl/s1600/evolution-948285.jpeg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="113" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZxuygIYsoFcVheqcozT3bKBC5u6IylDkqf_g_QllSAeZpL6jQAdmaX8hPtJZfKi_yjjM7ArnkDjugsOYu6jth9-godPXM_YHY2P1BsRh4GmBDNDYSPB-nuxbnE_c6rNKoxb5KMJnQ3gOl/s320/evolution-948285.jpeg" width="320" /></a></div>
<a href="http://en.wikipedia.org/wiki/Progressive_enhancement">Progressive enhancement</a> es una técnica que consiste comenzar con el contenido, usando los tags de HTML para estructurarlo (títulos, párrafos, listas, etc) y luego ir agregando el diseño y "comportamiento" mediante CSS y JavaScript.<br />
<br />
Pueden encontrar una buena introducción en <a href="http://sixrevisions.com/web-development/progressive-enhancement/">este articulo</a>.<br />
<br />
Claro que en la pequeña aplicación JavaScript no hay contenido :), en fin el objetivo era aprender y practicar técnicas útiles para el desarrollo web.<br />
<br />
<br />
<b>CSS</b><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnepvVwQt-bciwusX20uhqkmSmUQoAw5vuis_NMERD9DoSf7NRdGIxa4OJ6A7QYSUdCM2xSPI1mztEGs0CrLMcwnA2piAqWpmuP9f__-y66o1eCoZhHYtFLBmJe136MOj8pMrGzt-gob4q/s1600/logo.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnepvVwQt-bciwusX20uhqkmSmUQoAw5vuis_NMERD9DoSf7NRdGIxa4OJ6A7QYSUdCM2xSPI1mztEGs0CrLMcwnA2piAqWpmuP9f__-y66o1eCoZhHYtFLBmJe136MOj8pMrGzt-gob4q/s1600/logo.png" /></a></div>
Los stylesheets son sumamente engorrosos de mantener. Afortunadamente existen lenguajes de macros que permiten generar stylesheets usando constantes y funciones.<br />
Uno de ellos es <a href="http://lesscss.org/">LESS</a>, que lo elegí por dos razones: pueden usar JavaScript en el cliente para hacer pruebas y para aplicaciones Java cuentan con <a href="http://www.asual.com/lesscss/">un servlet</a>.<br />
Otra alternativa para quienes usan Ruby es <a href="http://sass-lang.com/">Sass</a>.<br />
<br />
<br />
<b>Canvas y gráficos dinámicos</b><br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaMeNIehdg6juEH1I9o-qGagOwNnRverpT_EOClzapSmd4jqBrpeUT58XG3i5q2Wm92MEHQ2IuTbF4HsiM6u4KoIyJzfHvq5NNQ05rImyPdvHBC5OA9OvH2qGLoIRiaBDcu1vQRG_Xykma/s1600/tiger.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaMeNIehdg6juEH1I9o-qGagOwNnRverpT_EOClzapSmd4jqBrpeUT58XG3i5q2Wm92MEHQ2IuTbF4HsiM6u4KoIyJzfHvq5NNQ05rImyPdvHBC5OA9OvH2qGLoIRiaBDcu1vQRG_Xykma/s200/tiger.png" width="195" /></a></div>
Para los gráficos comence usando un <a href="https://developer.mozilla.org/en/HTML/Canvas">Canvas</a>.<br />
Lamentablemente el API de Canvas es en cierto sentido de "bajo" nivel: provee primitivas para dibujar directamente, pero carece de algunas abstracciones convenientes.<br />
Luego de frustrarme (por alguna razón que no pude encontrar, los gradients en el stylesheet que uso modifican el estilo de dibujo del Canvas... algo muy engorrozo que me paso tanto Chrome como en Firefox) decidí buscar algun framework JavaScript que me facilite las cosas.<br />
Entre <a href="http://www.admixweb.com/2010/01/07/17-javascript-animation-frameworks-worth-to-seing/">los frameworks que estube mirando</a> me encontré a gusto con <a href="http://raphaeljs.com/">raphaeljs</a>.<br />
No usa Canvas si no SVG (que al parecer no esta disponible en algunos browsers para smartphones).<br />
Pero el API me pareció simple e ideal para hacer visualizaciones en HTML.<br />
<br />
<br />
<b>Local storage</b><br />
Guardar y retornar información local en HTML5 es extremadamente fácil:<br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">localStorage.setItem("key", "hello");</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">var value = localStorage.getItem("key");</span><br />
<br />
Claro que este "map" es bastante rudimentario, hay <a href="http://diveintohtml5.org/storage.html#future">otras opciones</a> pero funcionan solo en ciertos browsers.<br />
Aunque no lo use en este ejemplo, también pueden proveerle al browser información de que archivos mantener localmente para <a href="http://diveintohtml5.org/offline.html#manifest">que la aplicación funcione offline</a>.<br />
<br />
<br />
<b>Debugging y profilling</b><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQO05r5KFMmqCik52t5ngUUqQoeV42mflXz1UNSEJlnnFj278cYYmmGbpIpwZaEFDuxhCEMvXK94PPqI3lIFXC1d-oBcKIS1kbQIh9zUTsUIGcf4JI5oJ7V0wq933lSxwXFSC3EUuRhLMz/s1600/Flaming-firebug-logo-with-text.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="60" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQO05r5KFMmqCik52t5ngUUqQoeV42mflXz1UNSEJlnnFj278cYYmmGbpIpwZaEFDuxhCEMvXK94PPqI3lIFXC1d-oBcKIS1kbQIh9zUTsUIGcf4JI5oJ7V0wq933lSxwXFSC3EUuRhLMz/s200/Flaming-firebug-logo-with-text.png" width="200" /></a></div>
Con la ayuda de Diego Camera, conoci algunas herramientas de debugging y profiling de Google Chrome.<br />
Tanto si usan Chrome o no, cuentan con la extensión <a href="http://getfirebug.com/">FireBug </a>que les agrega un <a href="http://getfirebug.com/wiki/index.php/Console_API">API de consola</a> extremadamente útil para hacer debugging y profiling (en Chrome no necesitan instalar una extensión para acceder a este API).<br />
<br />
Para destacar: <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">console.dir(object)</span> les permite inspeccionar rápidamente el objeto pasado como parámetro.<br />
<br />
Una cosa que me gusta de trabajar con estas herramientas es que me dio casi la misma sensación que en <a href="http://en.wikipedia.org/wiki/Smalltalk">Smalltalk</a>: uno puede inspeccionar y modificar en "entorno" en cualquier momento. Quizás en algún momento con proyectos como <a href="http://ace.ajax.org/">Ace </a>(antes Mozilla Bespin/Skywriter) y <a href="http://www.eclipse.org/orion/">Eclipse Orion</a> se llegue a lo que hacían los entornos de Smalltalk hace más de 20años ;-)<br />
<br />
<b>Para terminar....</b><br />
Este post solo tiene algunas "puntas" a mirar para aprender más sobre JavaScript y HTML5.<br />
<br />
Si están interesados en hacer algo para aprender, <a href="http://dl.dropbox.com/u/1528235/glife.war">la implementación del juego de la vida</a> que presente en el post anterior tiene muchas cosas pendientes, y con distintos niveles de complejidad:<br />
<ul>
<li>Cada celda tiene su propio event handler, se podría usar un unico event handler para ahorrar recursos.</li>
<li>Las celdas se pintan haciendo click, lo cual es bastante incomodo si quieren pintar más de una celda. Se podrían cambiar los event handlers para permitirlo.</li>
<li>La función de "Save/Load" solo permite grabar una instancia, se podria modificar para soportar mas de una.</li>
<li>Seria útil poder insertar <a href="http://www.radicaleye.com/lifepage/#browse">patrones de celdas predefinidos</a></li>
<li>Se puede cambiar la implementación para usar el tag canvas en lugar de raphaeljs</li>
<li>... y si son unos "grosos" de los algoritmos, podrían simular que el "mundo" es infinito en lugar de hacer la simplificación que hice</li>
</ul>Diego Fernándezhttp://www.blogger.com/profile/11282484801526539091noreply@blogger.com0tag:blogger.com,1999:blog-1951692453295043766.post-13035083253836133602011-03-05T03:24:00.005-03:002011-03-05T03:31:04.637-03:00Juego de la vida (1ra parte)<br />
Hace poco, conocí a través del libro "<a href="http://es.wikipedia.org/wiki/El_gran_dise%C3%B1o">El gran diseño</a>" de <a href="http://es.wikipedia.org/wiki/Stephen_Hawking">Stephen Hawking</a>, el <a href="http://es.wikipedia.org/wiki/Juego_de_la_vida">"juego de la vida" de Conway</a>.<br />
<br />
Este "juego" -entre comillas- me resultaba conocido, ya que en alguna vieja distribución de Linux, había visto <a href="http://glife.sourceforge.net/">algo similar</a>. Pero hasta hace unos días no tenia ni la menor idea de que se trataba.<br />
<br />
Pero basta de misterios, les cuento muy brevemente de que trata.<br />
<br />
La idea es que uno tiene una gran grilla con celdas. Las celdas tienen una configuración inicial: vivas (activadas) o muertas (desactivadas). A partir de esta configuración inicial se deja correr el tiempo -que son pasos discretos. A cada paso se determina que celdas nacen o mueren según unas reglas muy simples:<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<ul>
<li>Si la celda esta viva pero no tiene vecinos: <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqxC46W6MCrwmPqRul6a1eSTst5BFHyN-gXLGPvbdSuOGOt7Sq-RH7pKTt3h5-Uw8oCMXJjvqtfb2CEOC3w16NIzMz2F63uXJ8oCZhfANcVb7tTkfPxxFCyq5Ik4aSLTMM2YfhdMsqZLPD/s1600/one.png"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqxC46W6MCrwmPqRul6a1eSTst5BFHyN-gXLGPvbdSuOGOt7Sq-RH7pKTt3h5-Uw8oCMXJjvqtfb2CEOC3w16NIzMz2F63uXJ8oCZhfANcVb7tTkfPxxFCyq5Ik4aSLTMM2YfhdMsqZLPD/s1600/one.png" style="cursor: move;" /></a>, en la siguiente generación muere: <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLsVGGEOp8qLw-Ju86oQMfYwZaiv1zAQA1WQAGBfyvubltDKUvDPKro-OErJM4pVjpS3wz1MMiHz3mzS1WkxIDLHrr7LdExelF5JVFFuONLrFTrbetR4eIqsDrUY3swaesJwUvlSgHlW-9/s1600/zero.png"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLsVGGEOp8qLw-Ju86oQMfYwZaiv1zAQA1WQAGBfyvubltDKUvDPKro-OErJM4pVjpS3wz1MMiHz3mzS1WkxIDLHrr7LdExelF5JVFFuONLrFTrbetR4eIqsDrUY3swaesJwUvlSgHlW-9/s1600/zero.png" /></a> </li>
<li>Si la celda esta viva pero tiene entre 2 o 3 vecinos: <img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6-XEQ82hnHOdXnHVvcrX1a9xqMqDEy7j-hkfHHJKdAir_u_RDyxnqM2Dsz7nx6YH6zEigNlqb-fcTWuMJPLPA1f7R9UXFvSRKQ_1jfxFGQIL7Ce7m-GWIOhEmbgL5tenJeIGzZGq2lZDx/s1600/twonb.png" />, en la siguiente generación permanece viva: <img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8Hb_t0a7HjPDhJCcw5G0CoJKdUhxWmXd2ms9Y1fZzq-JabAXfvnh82ngq19VG76jE6Y71EQXUPRz-MWLSBqemw0vk_WUMgxLqbuxjQv28I83p7J6xUXIgWwEhzLq94yRqUAQgDU4Q2TD8/s1600/one-line.png" style="cursor: move;" /></li>
<li>Si la celda esta muerta pero tiene 3 vecinos: <img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdhr6ByNjWfQ099s4iHCwioBLoFgyU8n4U8N8cxkagXLJIy_TDco69xp7-_s_xk30jrfY_9JeWgjus1dSkCVG7kAVdNJvB25ClNAPo1Q9W4yc-pelor1H7WD3XAZnP6268zaNXA2eoFL05/s1600/twonb-2.png" style="cursor: move;" />, en la siguiente generación se transforma en una celda viva: <img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJ6W2qSIaEEx7Cp5SOs3YewjgmYXNkz-hlbJlEmBE3Ye5Konigy_ovC8tmGNNJZn3XxU05h1p1Y_-LR77ysC9HY5oy-wJbWvgsW4HjAm7lReIpKQMVoCs5LBHqVW5icFwFZb2b7dB0WHRA/s1600/twonb-3.PNG" /></li>
<li>Si la celda tiene más de 3 vecinos, muere.</li>
</ul>
<div>
<br /></div>
<div>
Notar que cuando se evalúa el estado de las celdas, es como si se sacara una foto: las reglas se evalúan en base al estado de esa foto.</div>
<div>
<br /></div>
Hasta acá nada interesante, ¿Donde esta la gracia de todo esto?<br />
La gracia esta en que dependiendo de la configuración inicial, si uno deja correr el "tiempo" las celdas cobran vida. Hasta el punto que es posible simular <a href="http://rendell-attic.org/gol/tm.htm">una maquina de Turing</a>!<br />
<br />
El aprendizaje de todo esto, es que "formas de vida" muy simples pueden dar lugar a comportamientos muy complejos. Algo que menciona <a href="http://es.wikipedia.org/wiki/Douglas_Hofstadter">Douglas Hofstadter</a> en su libro <a href="http://es.wikipedia.org/wiki/G%C3%B6del,_Escher,_Bach:_un_Eterno_y_Gr%C3%A1cil_Bucle">Gödel-Escher-Bach</a> cuando hace la analogía entre las <a href="http://en.wikipedia.org/wiki/Neuron">neuronas </a>y las colonias de hormigas.<br />
<br />
Como ven esto es algo muy interesante y simple de implementar a la vez :)<br />
Por eso lo use como excusa para hacer algunos experimentos con JavaScript, experimento que pueden bajar de <a href="http://dl.dropbox.com/u/1528235/glife.war">este link</a>.<br />
<br />
<blockquote>
<b>Nota:</b><br />
El link contiene un <a href="http://en.wikipedia.org/wiki/WAR_file_format_(Sun)">WAR </a>(Java Web Application Archive) que no es más que un archivo .zip.<br />
<b>Pero no necesitan Java, ni <a href="http://tomcat.apache.org/">Tomcat </a>o <a href="http://jetty.codehaus.org/jetty/">Jetty </a>para probarla. </b></blockquote>
<blockquote>
La razón por la que use un WAR es la siguiente: quisé hacer tambien el experimento de usar <a href="http://lesscss.org/">LESS </a>(un lenguaje de macros para CSS -altamente recomendable).<br />
LESS incluye una version JavaScript que pueden usar en el cliente... pero desafortunadamente usa un <a href="http://es.wikipedia.org/wiki/XMLHttpRequest">XMLHttpRequest </a>para bajar el CSS, y por razones de seguridad browsers como Chrome lo filtran.<br />
Por eso termine haciendo una aplicación web, que usa un <a href="http://www.asual.com/lesscss/">servlet para LESS</a> (tenia otras opciones, pero quería probar LESS y esta opción era más sencilla que instalar <a href="http://nodejs.org/">Node.js</a> en Windows).<br />
<b>El uso de LESS no es necesario, pueden jugar con el ejemplo sin necesidad de levantar una aplicación web: abran directamente el archivo index.html dentro del war.</b></blockquote>
En el próximo post les cuento como desarrolle esta pequeña implementación (que usa <a href="http://jquery.com/">jQuery</a>, <a href="http://raphaeljs.com/">Raphael </a>y <a href="http://diveintohtml5.org/storage.html">local storage</a>).<br />
<br />
Mientras tanto les recomiendo jugar con patrones como este (pueden usar la opción de Save/Load para guardar/recuperar el patrón del local storage):<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOZ1HO2eQFo1c26ouuBosJHE1fDAFk_VAujPEvphTchPupumGv1x744E-dGiKgBO3t-zWQtbixQ_6tAT3b3WOJXX7HJYKgKaxGwqJPosg092uAa96K-ysAmcy2YzGwi6xqOi23RIQfJwyH/s1600/pattern.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOZ1HO2eQFo1c26ouuBosJHE1fDAFk_VAujPEvphTchPupumGv1x744E-dGiKgBO3t-zWQtbixQ_6tAT3b3WOJXX7HJYKgKaxGwqJPosg092uAa96K-ysAmcy2YzGwi6xqOi23RIQfJwyH/s1600/pattern.png" /></a></div>
<br />Diego Fernándezhttp://www.blogger.com/profile/11282484801526539091noreply@blogger.com0tag:blogger.com,1999:blog-1951692453295043766.post-86356306065950317662011-02-23T19:19:00.002-03:002011-02-25T17:27:43.985-03:00Reingeniería de software (2da parte)En el <a href="http://diegacho.blogspot.com/2011/01/reingenieria-de-software-1era-parte.html">post anterior</a> presente los términos utilizados comúnmente en reingeniería de software. En este voy a centrarme en una herramienta para <i>design recovery -</i>desarrollada por el <a href="http://scg.unibe.ch/">Software Composition Group</a> de la <a href="http://www.unibe.ch/">universidad de Berna</a>- llamada <a href="http://www.moosetechnology.org/">Moose</a>.<br />
<br />
Moose esta compuesto de varios frameworks, de los cuales voy a mencionar los que creo principales para la tarea de <i>design recovery</i>:<br />
<br />
<b></b><br />
<ul><b>
<li><span class="Apple-style-span" style="font-weight: normal;"><b>FAMIX*:</b></span><span class="Apple-style-span" style="font-weight: normal;"> es un modelo que representa entidades de código y sus relaciones (para su posterior análisis estático). </span><span class="Apple-style-span" style="font-weight: normal;"><i>* En la documentación y papers que lei sobre Moose se me hizo un tanto confusa la diferencia entre FAME y FAMIX. Por lo que pude entender FAMIX es el nombre metamodelo y FAME la implementación en Moose para el intercambio de estos meta modelos.</i></span></li>
<li><span class="Apple-style-span" style="font-weight: normal;"><b>Moose Core:</b></span><span class="Apple-style-span" style="font-weight: normal;"> trabaja sobre el meta modelo de FAMIX permitiendo hacer </span><span class="Apple-style-span" style="font-weight: normal;"><a href="http://www.themoosebook.org/book/externals/ui/finder">consultas usando Smalltalk</a></span><span class="Apple-style-span" style="font-weight: normal;">.</span></li>
<li><b>Mondrian:</b> es un framework de visualización, que facilita la creación de vistas polimetricas (vistas polimetricas? más a continuación).</li>
</b></ul>
<b>
</b><br />
<div>
<i></i><br />
Hasta acá todo muy lindo con la lista de herramientas, pero... <i><b>¿Cómo me ayudan a entender un sistema?</b></i><br />
<br />
Cuando vi Moose por primera vez me hice la misma pregunta. En realidad, como no estaba familiarizado con las herramientas de <i>design recovery</i> mi pregunta fue mucho más básica: ¿Para que sirve Moose?<br />
<br />
Si bien hay <a href="http://www.themoosebook.org/book/table-of-contents">un libro</a> online (estilo wiki), no me quedaba claro como se usaba la herramienta hasta que leí la <a href="http://www.inf.usi.ch/faculty/lanza/Downloads/Lanz03b.pdf">tesis de doctorado de Michele Lanza</a> (es especial el capitulo 4).<br />
Para quienes quieran buscar ejemplos de como usar las herramientas de Moose, les recomiendo leer esta tesis. Y para los que no, les cuento brevemente de que trata.<br />
<br />
Como mencione en el post anterior el problema principal de la reingeniería no es hacer el rediseño (o refactoring), si no entender lo que hay. Y como los sistemas suelen ser enormes, es necesario generar y visualizar de alguna manera información de resumen que ayude a entender las cosas a alto nivel, o al menos guiarnos hacia los lugares de importancia.<br />
<br />
Con este objetivo Lanza trabajo en un tipo de visualización llamado vistas polimetricas que mezcla entidades de software y sus relaciones, junto con métricas de software. (en <a href="http://scg.unibe.ch/archive/papers/Lanz03dTSEPolymetric.pdf">este articulo</a> o en el libro <a href="http://www.moosetechnology.org/publications/oomip">Object-Oriented Metrics in Practice</a> pueden encontrar una explicación detallada de las vistas polimetricas).<br />
<br />
La idea de mezclar métricas en la visualización, es la de poder identificar visualmente posibles candidatos a mirar. Acá van algunos ejemplos:<br />
<br />
<b>System Hotspots View</b><br />
Da una idea general del tamaño del sistema. En este gráfico cada rectángulo representa una clase, y su tamaño esta dado por el número de métodos. (Los cuadrados grises son meta clases, esto se hizo sobre un sistema en Smalltalk, en general tener muchos métodos en la meta clase es un indicador que están faltando abstracciones adecuadas -es casi como tener muchos métodos <i>static </i>en Java):<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLjhyphenhyphenjLtWeE85A8I633P01dH3v4pL6mEHHxSuiWPe61S_zQBrM3NEru4ae3AzXYHq9lYznTbU8UUh4yONlKdHltV092y6q1pcdspUnqFxjiMhbifkrPYG2YPgDH5Ufjwxyb-rb_EgENDM6/s1600/systemHotspots.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="225" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLjhyphenhyphenjLtWeE85A8I633P01dH3v4pL6mEHHxSuiWPe61S_zQBrM3NEru4ae3AzXYHq9lYznTbU8UUh4yONlKdHltV092y6q1pcdspUnqFxjiMhbifkrPYG2YPgDH5Ufjwxyb-rb_EgENDM6/s640/systemHotspots.png" width="640" /></a></div>
<br />
<br />
<b>System Complexity View</b><br />
Muestra las relaciones de herencia entre clases y los tamaños de cada una de ellas (ancho: número de atributos, alto: número de métodos).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKgEivrQzsyVmiUNAqaCvjdfWtYGwhFFcgGTKUokNFtrF-Yc9nXSSuTeJYV18J7ruJQd138OZTdGLEgSJSmuK8McowmqvmEucff9e6f-TCMKyINoj-c1SPvBGqHFGeHmSa5KkPjQgL8Ts5/s1600/systemComplexity.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="284" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKgEivrQzsyVmiUNAqaCvjdfWtYGwhFFcgGTKUokNFtrF-Yc9nXSSuTeJYV18J7ruJQd138OZTdGLEgSJSmuK8McowmqvmEucff9e6f-TCMKyINoj-c1SPvBGqHFGeHmSa5KkPjQgL8Ts5/s640/systemComplexity.png" width="640" /></a></div>
<br />
<b>Inheritance Classification View</b><br />
Las dos vistas anteriores fueron un ejemplo de lo que Lanza llama "first contact" views, es decir sirven para tener un pantallazo muy general, en cambio esta vista se aplica a una jerarquía de clases.<br />
La forma de árbol muestra relaciones de herencia, el ancho de las cajas la cantidad de métodos agregados, el alto la cantidad de métodos sobre-escritos y el color (en escala de grises) la cantidad de métodos sobre-escritos que llaman a <i>super</i>.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYySHq1mEzArvSKbdj6BZ5pb8nxeN2zWZUDyqjxplZW7YXQ6iIZrfSHh9G6x_G9OjoWJ4XYqwmeE5_V7IuOSkwMHOpwJbZ9jjR-XuTGN9QiKgwQaeuaEbJZw3Qv8j0DxeYCtQ3u_DShL5T/s1600/inheritance.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="206" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYySHq1mEzArvSKbdj6BZ5pb8nxeN2zWZUDyqjxplZW7YXQ6iIZrfSHh9G6x_G9OjoWJ4XYqwmeE5_V7IuOSkwMHOpwJbZ9jjR-XuTGN9QiKgwQaeuaEbJZw3Qv8j0DxeYCtQ3u_DShL5T/s400/inheritance.png" width="400" /></a></div>
<br />
<br />
Estos fueron algunos ejemplos, hay más vistas utilizadas para obtener información a distintos niveles de detalle. Cabe aclarar que la idea es que estas herramientas de visualización sean interactivas (ej. pasar el mouse sobre cada caja nos dice la clase), de nada sirve tener cajas de colores si no sabemos de que parte del sistema nos esta hablando.<br />
<br />
¿Donde entra Moose en todo esto? Moose es un framework que permite crear estas vistas y explotar la información. "Out of the box" no es una herramienta automática para hacer una evaluación del sistema, si no más bien un framework, que se puede automatizar (usando Smalltalk) e integrar a sistemas de integración continúa (como Hudson o mejor dicho Jenkins).<br />
<blockquote>
<b>Nota:</b> Moose esta hecho en Smalltalk, pero puede analizar sistemas en Java usando un conversor de Java al formato FAMIX.</blockquote>
¿Existen otras herramientas de este estilo?<br />
Mas o menos. Moose es un framework general, lo más parecido a un framework general de este tipo es una vieja herramienta llamada <a href="http://www.rigi.csc.uvic.ca/Pages/description/whatitis.html">Rigi </a>(que ya parece estar obsoleta - al menos no se actualiza desde hace mucho).<br />
<br />
Si existen sistemas que proveen visualizaciones particulares, por ejemplo <a href="http://www.inf.usi.ch/phd/wettel/codecity.html">CodeCity </a>es un software muy interesante que lee modelos de FAMIX y realiza vistas basadas en métricas pero utilizando una metáfora de ciudad en 3d, por ejemplo la siguiente es una representación del JDK (versión 1.5) realizada con CodeCity<br />
<blockquote>
Si mal no recuerdo: alto=número de métodos, ancho=número de atributos, los colores varían según lo que se quiera ver por ejemplo se pueden utilizar para resaltar "design disharmonies" -otro término para "code smells". Las "manzanas" de la ciudad representan paquetes.</blockquote>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8CJ-Re0QiVH47RVaCp7dME-FfAjqfC6oewuWgq9xTZVLo4sLP9QOZhvjOSFni41JVgDKoC1Fas7If_4iqylSK4eO2xWY8HOuSOPrH_2J1C8x9BsAInehpmI-GLqrjEDN2Mkn3GGGs6Wpg/s1600/jdk.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="347" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8CJ-Re0QiVH47RVaCp7dME-FfAjqfC6oewuWgq9xTZVLo4sLP9QOZhvjOSFni41JVgDKoC1Fas7If_4iqylSK4eO2xWY8HOuSOPrH_2J1C8x9BsAInehpmI-GLqrjEDN2Mkn3GGGs6Wpg/s640/jdk.png" width="640" /></a></div>
<br />
<br />
<br /></div>Diego Fernándezhttp://www.blogger.com/profile/11282484801526539091noreply@blogger.com2tag:blogger.com,1999:blog-1951692453295043766.post-80938856790694065632011-01-08T16:46:00.001-03:002011-01-08T17:03:39.619-03:00Reingeniería de software (1era parte)<span class="Apple-style-span" style="font-size: 15px; white-space: pre-wrap;">Diseñar un sistema desde cero es una actividad creativa y entretenida. Elegimos la tecnología más adecuada, pensamos un diseño que cumpla con las necesidades e intentamos hacer las cosas lo mejor posible. </span><br />
<div style="background-color: transparent; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: inherit;"><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Luego: nuevos requisitos, tiempos ajustados, parches en el diseño, nuevos desarrolladores, más funcionalidad, interacción con otros sistemas y tecnologías; más parches, ese framework que era novedoso ya es obsoleto y problemático; cambios de presupuesto, rotación del equipo... </span><br /><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Y así, nuestro hermoso “bebe” devino en una bestia inmanejable. </span><br /><a href="http://en.wikipedia.org/wiki/No_Silver_Bullet"><span style="background-color: transparent; color: #000099; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">Hombre lobo</span></a><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> o </span><a href="http://www.laputan.org/mud/"><span style="background-color: transparent; color: #000099; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">bola de lodo</span></a><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, no importa, cambiamos de trabajo/proyecto y ahora empezamos de nuevo. </span><br /><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Ilusos -todo vuelve- ahora cae a nuestras manos una bestia. </span><br /><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="background-color: transparent; color: black; font-size: 11pt; font-style: italic; font-weight: bold; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Primer impulso: dinamitemos la cosa y empecemos de cero.</span><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">No es mala idea, hay quienes estiman que el costo de mantenimiento va del 50% al 75% del costo total de un sistema de software (</span><a href="http://portal.acm.org/citation.cfm?id=203406"><span style="background-color: transparent; color: #000099; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">Davis</span></a><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><a href="http://www.amazon.com/Software-Engineering-7th-Ian-Sommerville/dp/0321210263"><span style="background-color: transparent; color: #000099; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">Sommervielle</span></a><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">).</span><br /><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">El problema es que el costo de desarrollo no es la única variable a tener en cuenta: desde el punto de vista de los negocios comenzar de nuevo puede ser una maniobra de mucho riesgo.</span><br /><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="background-color: transparent; color: black; font-size: 11pt; font-style: italic; font-weight: bold; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">¿Por donde empezar?</span><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Bienvenidos a la reingeniería de software.</span><br /><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Para adentrarnos en este tema vamos a comenzar por ponerle nombre a algunas cosas que probablemente ya sabemos -una tarea muy aburrida pero útil a la hora de buscar y compartir información.</span><br /><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">La mayoría de los investigadores en este área utilizan los términos descriptos en el </span><a href="http://www.computer.org/portal/web/csdl/doi/10.1109/52.43044"><span style="background-color: transparent; color: #000099; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">articulo de Chikofsky y Cross</span></a><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> y en el </span><a href="http://ieeexplore.ieee.org/xpl/freeabs_all.jsp?arnumber=723185"><span style="background-color: transparent; color: #000099; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">paper de Kazman, Woods y Carriere</span></a><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">:</span></span></div>
<div style="background-color: transparent; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-family: inherit;"><span style="background-color: transparent; color: black; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline;"></span><span class="Apple-style-span" style="font-size: 15px; white-space: pre-wrap;"><br /></span></span><br />
<div style="margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span class="Apple-style-span" style="font-family: inherit;"><span style="background-color: transparent; color: black; font-size: 10pt; font-style: italic; font-weight: bold; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Reengineering.</span><span style="background-color: transparent; color: black; font-size: 10pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> Reengineering, also know as both renovation and reclamation, is the examination and alteration of a subject system to reconstitute it in a new form and subsequent implementation of the new form.</span></span></div>
<div style="margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span class="Apple-style-span" style="font-family: inherit;"><span style="background-color: transparent; color: black; font-size: 10pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Reengineering generaly includes some form of </span><span style="background-color: transparent; color: black; font-size: 10pt; font-style: italic; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">reverse engineering</span><span style="background-color: transparent; color: black; font-size: 10pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> (to archive a more abstract description) followed by some form of forward engineering or re-structuring.</span><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"></span></span></div>
<span class="Apple-style-span" style="font-family: inherit;"><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Es decir reingeniería es alterar un sistema existente y cambiarlo de forma para permitir su evolución. Esta tarea incluye entender lo que ya existe mediante ingeniería reversa:</span></span></div>
<div style="background-color: transparent; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<span class="Apple-style-span" style="font-size: 15px; white-space: pre-wrap;"><br /></span></div>
<div style="background-color: transparent; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">
<div style="margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span class="Apple-style-span" style="font-family: inherit;"><span style="background-color: transparent; color: black; font-size: 10pt; font-style: italic; font-weight: bold; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Reverse engineering.</span><span style="background-color: transparent; color: black; font-size: 10pt; font-style: italic; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> Is the process of analyzing a subject system to</span></span></div>
<div style="margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span style="background-color: transparent; color: black; font-size: 10pt; font-style: italic; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-style-span" style="font-family: inherit;">identify the system’s components and their interrelationships and</span></span></div>
<div style="margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span class="Apple-style-span" style="font-family: inherit;"><span style="background-color: transparent; color: black; font-size: 10pt; font-style: italic; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">create representations of the system in another form or at higher level of abstraction.</span><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"></span></span></div>
<span class="Apple-style-span" style="font-family: inherit;"><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">La ingerniería reversa solo abarca la parte de examinar un sistema existente. Esta se puede dividir en dos sub-areas:</span><br /><span style="background-color: transparent; color: black; font-size: 10pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"></span></span><br />
<div style="margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span class="Apple-style-span" style="font-family: inherit;"><span style="background-color: transparent; color: black; font-size: 10pt; font-style: italic; font-weight: bold; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Redocumentation.</span><span style="background-color: transparent; color: black; font-size: 10pt; font-style: italic; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> </span><span style="background-color: transparent; color: black; font-size: 10pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Redocumentation is the creation or revision of a semantically equivalent representation within the same abstraction level.</span></span></div>
<span class="Apple-style-span" style="font-family: inherit;"><span style="background-color: transparent; color: black; font-size: 10pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"></span></span><br />
<div style="margin-bottom: 0pt; margin-left: 36pt; margin-top: 0pt;">
<span class="Apple-style-span" style="font-family: inherit;"><span style="background-color: transparent; color: black; font-size: 10pt; font-style: italic; font-weight: bold; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Design Recovery.</span><span style="background-color: transparent; color: black; font-size: 10pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> is a subset of reverse engineering in which domain knowledge, external information, and deduction or fuzzy reasoning are added to the observations of the subject system to identify meaningful higher level abstractions beyond those obtained directly by examining the system itself.</span><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"></span></span></div>
<span class="Apple-style-span" style="font-family: inherit;"><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">En ejemplos: </span><span style="background-color: transparent; color: black; font-size: 11pt; font-style: italic; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Redocumentation</span><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> es lo que hacen herramientas como Enterprise Architect que generan diagramas UML de clase a partir del código existente. El problema con este tipo de herramientas es que están al mismo nivel de abstracción que el código actual: recorrer un diagrama de clases de este tipo no es muy diferente a utilizar un “browser” de código en un IDE.</span><br /><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Sin embargo para poder comprender un sistema grande se necesita poder recrear otro tipo de información. Por ejemplo poder identificar módulos (más alla de la división de paquetes en el código) y relacionarlos con la funcionalidad o poder entender la evolución del sistema y su relación con diferentes refactorings, funcionalidad y defectos. </span><br /><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="background-color: transparent; color: black; font-size: 11pt; font-style: italic; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Design Recovery</span><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> se refiere a este segundo tipo de herramientas, y es donde se concentra el research en el área de reingeniería.</span><br /><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Para poder llegar a hacer herramientas de </span><span style="background-color: transparent; color: black; font-size: 11pt; font-style: italic; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Design Recovery</span><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, primero es necesario tener herramientas que puedan analizar el código. Este tipo de herramientas se llaman herramientas de </span><span style="background-color: transparent; color: black; font-size: 11pt; font-style: italic; font-weight: bold; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Program Analysis</span><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> y las hay de tres tipos: estático (analizan la estructura del código), dinámico (analizan las interacciones en runtime) e histórico (analizan como el código fue cambiando historicamente). Hay varios desafios en el area de program analysis, para hacer un resumen de ellos:</span></span><br />
<ul>
<li style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><span class="Apple-style-span" style="font-family: inherit;"><span style="background-color: transparent; color: black; font-size: 11pt; font-style: italic; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">análisis estático: </span><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">lidiar con diferentes lenguajes de programación o scripting en un mismo sistema.</span></span></li>
<li style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><span class="Apple-style-span" style="font-family: inherit;"><span style="background-color: transparent; color: black; font-size: 11pt; font-style: italic; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">análisis dinámico:</span><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> lidiar con enormes cantidades de información, un simple “trace” de una aplicación real puede ser enorme.</span></span></li>
<li style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><span class="Apple-style-span" style="font-family: inherit;"><span style="background-color: transparent; color: black; font-size: 11pt; font-style: italic; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">análisis histórico: </span><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">lidiar con herramientas de SCM y el hecho que los “eventos” en el tiempo no son registrados de una forma uniforme.</span></span></li>
</ul>
<span class="Apple-style-span" style="font-family: inherit;"><br /><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Hacer </span><span style="background-color: transparent; color: black; font-size: 11pt; font-style: italic; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Design Recovery</span><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> significa buscar crear un modelo mental del sistema que permita comprenderlo, lo cual implica buscar formas de visualizar el software. Esto nos lleva a las </span><span style="background-color: transparent; color: black; font-size: 11pt; font-style: italic; font-weight: bold; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">herramientas de visualización.</span><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Crear visualizaciones de un software no es fácil, por que hay muchas “dimensiones” e interrelaciones, asi que un área de estudio importante es como crear visualizaciones que permitan entender un sistema grande más facilmente.</span><br /><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">En el próximo post voy a hablar un poco sobre herramientas de visualización.</span><br /><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> </span><br /><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Por ahora que ya saben los términos usados en este área de estudio les dejo algunos links a conferencias relacionadas con reingeniería de software:</span></span><br />
<ul>
<li style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><span class="Apple-style-span" style="font-family: inherit;"><a href="http://www.softvis.org/"><span style="background-color: transparent; color: #000099; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">SOFTVIS</span></a><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">: ACM Symposium on Software Visualization</span></span></li>
<li style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><span class="Apple-style-span" style="font-family: inherit;"><a href="http://web.soccerlab.polymtl.ca/wcre2010/"><span style="background-color: transparent; color: #000099; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">Working Conference on Reverse Engineering </span></a><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(WCRE)</span></span></li>
<li style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><span class="Apple-style-span" style="font-family: inherit;"><a href="http://conferences.computer.org/icsm/"><span style="background-color: transparent; color: #000099; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">International Conference on Software Maintenance </span></a><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(ICSM)</span></span></li>
<li style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><span class="Apple-style-span" style="font-family: inherit;"><a href="http://www.program-comprehension.org/"><span style="background-color: transparent; color: #000099; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">International Conference on Program Comprehesion</span></a><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> (ICPC)</span></span></li>
<li style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><span class="Apple-style-span" style="font-family: inherit;"><a href="http://www.ieee-scam.org/"><span style="background-color: transparent; color: #000099; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">International Workshop on Source Code Analysis and Manipulation </span></a><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(SCAM)</span></span></li>
<li style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; list-style-type: disc; text-decoration: none; vertical-align: baseline;"><span class="Apple-style-span" style="font-family: inherit;"><a href="http://swerl.tudelft.nl/bin/view/PCODA"><span style="background-color: transparent; color: #000099; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap;">Workshop on Program Comprehension through Dynamic Analysis </span></a><span style="background-color: transparent; color: black; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(PCODA)</span></span></li>
</ul>
</div>Diego Fernándezhttp://www.blogger.com/profile/11282484801526539091noreply@blogger.com0tag:blogger.com,1999:blog-1951692453295043766.post-91557382963388061422010-04-13T09:07:00.001-03:002010-04-13T09:08:13.839-03:00ComposiciónHace unos años atrás cuando dictaba cursos sobre POO y Java, di un ejercicio que consistía en diseñar el "checkout" de un Supermercado con soporte para ofertas del estilo "<i>lleve 2 y pague 1</i>".<br> La intención del ejercicio era que los alumnos piensen en como utilizar objetos para lidiar con la complejidad de aplicar las diferentes ofertas. <br> Esperaba también que se dieran cuenta que el precio de un producto depende de factores como: la fecha, el "mercado", ofertas u estrategias de negocio.<br> Imagine que comenzarían planteando la clase <i>Producto</i> con un "estilo base de datos":<br><br><div id="p5dz" style="text-align:center"><img src="http://yuml.me/diagram/scruffy/class/%5BProducto%7Cprecio%5D" style="height:59px;width:117px"></div><br> Y que a lo largo del ejercicio iban a llegar a una especie de "lista de precios":<br><br><div style="text-align:center"><img src="http://yuml.me/diagram/scruffy/class/%5BProducto%5D%5BListaDePrecios%7CprecioDe%28Producto%29%5D" style="height:141px;width:197px"></div><br> Sin embargo no fue como esperaba. Para uno de los alumnos la expresión "el producto tiene un precio" significaba que el un producto tenia que responder directamente su precio. Trate de justificar mi posición desde distintos puntos de vista, pero luego abandone la discusión.<br><i><b><br> Nota al margen:</b></i> Mi experiencia dando clases me enseño que es mejor no entrar en discusiones sin salida. Es preferible escuchar pacientemente y expresar cuales son los problemas del planteo desde distintos puntos de vista, con suerte la discusión evoluciona y se aprende mucho en el proceso. Pero si la discusión se estanca, es preferible abandonarla.<br><br> Escribo sobre este ejemplo después de tanto tiempo, por que veo que el problema de como expresar la relación de composición se repite en otros contextos. <br>Por ejemplo ¿Cómo modelarían que "un cliente que tiene contactos"? Una forma de hacerlo es:<br><br><div id="cein" style="text-align:center"><img src="http://docs.google.com/File?id=ddxm23r2_91hmcg7wrn_b" style="height:69px;width:328px"></div><br><br> Sin embargo, al hacer la traducción literal de este diagrama de clases, el modelo esta diciendo: "el cliente esta compuesto de contactos". La diferencia parece sutil, pero a medida que el sistema crece tiene consecuencias importantes:<br><ul><li>Agregar nuevos contactos implica modificar la instancia de cliente y trae consecuencias en la forma de asegurar que se cumplan las reglas de negocio, por ejemplo: que un cliente cambie su numero CUIT es raro y puede afectar a los sistemas de facturación; pero es esperable que sus contactos cambien seguido sin mayores consecuencias para el resto del sistema, entonces hay que distinguir entre "modificaciones pesadas" y "modificaciones livianas".<br></li><li> Transmitir el cliente por "el cable" implica transmitir sus contactos, y como esta información puede ser pesada uno tiene que usar alguna estrategia como un objeto que represente una vista del cliente sin sus contactos (<i><a href="http://en.wikipedia.org/wiki/Data_transfer_object" id="bvt5" title="DTO">DTO</a></i>), o bien hacer que la lista de contactos se cargue por demanda (<i><a href="http://en.wikipedia.org/wiki/Lazy_loading" id="k1j4" title="lazy load">lazy load</a></i>).</li></ul><br><b>Nota al margen:</b> Creo que uno de los causantes de confusión es el uso del verbo <i>tener</i>. En nuestra manera de expresarnos es natural decir "el producto tiene un precio", sin embargo el "tiene" de nuestro lenguaje no expresa necesariamente una relación de composición. <a href="http://en.wikipedia.org/wiki/Erich_Fromm" id="s57n" title="Erich Fromm">Erich Fromm</a>, escribió un libro muy bueno llamado <a href="http://en.wikipedia.org/wiki/To_Have_or_to_Be%3F" id="gjxf" title=""Tener o Ser"">"Tener o Ser"</a> que habla sobre la influencia del "tener" en la sociedad. Es curioso como el consumismo cambio nuestras formas de expresión, por ejemplo en lugar de decir "siento un dolor de cabeza" decimos "tengo un dolor de cabeza".<br><br> En cierto sentido el problema es similar al de Producto-Precio, una alternativa para expresar la relación del cliente con sus contactos es tener una "agenda de contactos" mediante la cual se pueden encontrar los contactos para un cliente:<br><br><div id="oa-l" style="text-align:center"><img src="http://docs.google.com/File?id=ddxm23r2_92tvcv6gcw_b" style="height:141px;width:208px"><br><br></div>¿Qué modelo expresa mejor la realidad?<br><br>Creo que pensar que un modelo es correcto por que expresa "la realidad" mejor que otro, sufre del problema de dar por sentado que nuestra percepción y traducción al sistema es absolutamente correcta: para uno de mis alumnos la realidad que "el producto tiene un precio", se traducía correctamente en "unProducto.getPrecio()".<br><br> Por eso es necesario tener en cuenta cuestiones que afectan a la percepción y traducción del problema:<br><ul><li>El verbo "tener" tiene un uso extremadamente ambiguo en nuestra cultura, por ejemplo es más adecuado decir que a un producto <i>se le asigna</i> un precio y quizás de esa forma ya vemos el dominio con otra perspectiva.</li><li>Los "ritmos" de cambio pueden hacer que sea más conveniente pensar en objetos distintos. Por ejemplo el precio varia con el tiempo y el entorno, pero el producto no; algo similar ocurre con el cliente y sus contactos. <br> Separar las cosas que cambian a distintas velocidades ayuda a evitar <a href="http://diegacho.blogspot.com/2009/08/side-effects.html" id="ep53" title="side effects">side effects</a> innecesarios y simplifica la aplicación de reglas de negocio.</li><li>Ver si existe en el dominio un intermediario en la relación, como por ejemplo una "lista de precios". Es claro que el software es un medio distinto: la lista de precio existe físicamente por que es una herramienta para definir y recordar el precio de un producto, y ese rol puede cumplirse en software sin necesidad de tener una "lista de precios" propiamente dicha. Sin embargo explorar esas analogías puede servir para ver conceptos del dominio.<br></li></ul><br>Diego Fernándezhttp://www.blogger.com/profile/11282484801526539091noreply@blogger.com2tag:blogger.com,1999:blog-1951692453295043766.post-55690461215010004802009-12-30T10:48:00.001-03:002009-12-30T10:50:58.127-03:00Libros que estuve leyendo: Historias de Terramar I y IIA veces los libros llegan a mi biblioteca de forma poco predecible.<img id="sz.a" style="width: 300px; height: 372px; float: right; margin-left: 1em; margin-right: 0pt;" src="http://docs.google.com/File?id=ddxm23r2_85fntjsqcp_b"><br><br>Si alguien me recomienda un libro, es fija que me olvido. Hasta que un tiempo despues, de paso por una librería, veo un titulo o autor y pienso: "este me suena, a ver...". <br><br>La cuestión es que <a title="después de leer El Aleph" href="http://diegacho.blogspot.com/2009/12/libros-que-estuve-leyendo-el-aleph.html" id="ckm:">después de leer El Aleph</a> tenía ganas de leer algo de <a title="literatura fantástica" href="http://es.wikipedia.org/wiki/Literatura_fant%C3%A1stica" id="lmg0">literatura fantástica</a> al estilo Señor de los anillos, es decir algo más pochoclero. <br>Entre a una librería de <a title="Av. Corrientes" href="http://es.wikipedia.org/wiki/Avenida_Corrientes" id="pop9">Av. Corrientes</a> a buscar eso y por alguna razón me acorde del titulo "<a title="Terramar" href="http://en.wikipedia.org/wiki/Earthsea" id="apn4">Terramar</a>".<br><br>Asi fue como compre <a title="Historias de Terramar I: Un mago de Terramar" href="http://es.wikipedia.org/wiki/Un_mago_de_Terramar" id="l642">Historias de Terramar I: Un mago de Terramar</a>. Aunque no es cierto que lo haya comprado en la primer librería que entré: por alguna razón no lo encontré en las típicas cadenas como Cuspide o Yenny.<br> <br>El libro me encanto. No tiene alusiones intelectuales complicadas, ni metáforas que requieran mucho análisis. Esta claro que buscaba "pocholo".<br><br>En comparación al Señor de los Anillos tiene muchos menos nombres extraños que memorizar ;), y sorprenden algunas pequeñas similitudes con Harry Potter, más teniendo en cuenta que Historias de Terramar fue editado en el año 1968. <br><br>Me gustaron también algunas cuestiones atípicas para este tipo de historias: el personaje principal no es el típico héroe que pelea contra los malos (hay un enemigo bien definido, pero contarlo seria arruinarles la historia) y no hay tampoco una división del estilo "elfos bonitos y buenos" vs "orcos feos y malos". Incluso me atrevería a decir que hay cierto transfondo <a title="Taoista" href="http://es.wikipedia.org/wiki/Tao%C3%ADsmo" id="nk_1">Taoista</a> en la historia.<br><br>En fin recomendable para: distenderse con una historia fantastica entretenida, regalarle a ese primo adolescente que no lee ni por casualidad, o leerlo en una tarde de verano a la sombra de un árbol después de comerse un asado.<br><br>Y como me gusto, después fui en busca de la segunda parte: <a title="Las tumbas de Atuan" href="http://es.wikipedia.org/wiki/Las_tumbas_de_Atuan" id="eo8g">Las tumbas de Atuan</a> (que también me costo encontrar en librerías). <br>No esta mal pero tiene un tono muy distinto: fue como escuchar <a title="Kid A" href="http://en.wikipedia.org/wiki/Kid_a" id="tvrd">Kid A</a> esperando la continuación de <a title="The Bends" href="http://en.wikipedia.org/wiki/The_Bends" id="i:2o">The Bends</a>.<br><br><b>NOTA:</b> Imagen de Ged (protagonista de la historia) obtenida del sitio <a title="www.fantasymundo.com" href="http://www.fantasymundo.com/galeria/imagen.php?imagen=5664" id="bk:s">www.fantasymundo.com</a><br>Diego Fernándezhttp://www.blogger.com/profile/11282484801526539091noreply@blogger.com1tag:blogger.com,1999:blog-1951692453295043766.post-45841822095300336092009-12-27T22:19:00.001-03:002009-12-27T22:19:34.586-03:00Libros que estuve leyendo: El AlephAntes de que termine el año quería comentarles algunos libros leí desde mi último <a title="post literario" href="http://diegacho.blogspot.com/2009/05/libros-que-estube-leyendo.html" id="utui">post literario</a>. <br>Son varios, pero siguiendo el consejo del <a title="Torto" href="http://www.torto.com.ar/" id="bmur">Torto</a>, voy a hacer posts breves y comentarlos de a uno. <br><br>Seguía con ganas de leer libros de ficción y tras varios autores anglosajones, preferí aventurarme en un autor local. Así que empecé a leer <a title="El Aleph" href="http://es.wikipedia.org/wiki/El_Aleph" id="w0m2">El Aleph</a>* de <a title="Borges" href="http://es.wikipedia.org/wiki/Jorge_Luis_Borges" id="mpr8">Borges</a>.<br><br>Claro que no conocía nada sobre Borges, y quizás por eso pensé que iba a leer "un autor local" si darme cuenta que él se caracterizo por ser el escritor más universal de la literatura argentina ;)<br><br><a title="El articulo sobre él" href="http://es.wikipedia.org/wiki/Jorge_Luis_Borges" id="g:e7">El articulo sobre él</a> de la Wikipedia en español parece estar muy completo y es una buena introducción.<br><br>Lo único que puedo decir es que el libro me pareció impecable, pero... no me engancho. No era su <i>momento</i>, y aunque leí la mayoría de los cuentos no llegue a terminarlo. <br><br>Me paso lo mismo la primera vez que leí <a title="Todos los fuegos el fuego" href="http://es.wikipedia.org/wiki/Todos_los_fuegos_el_fuego" id="s-ro">Todos los fuegos el fuego</a> de <a title="Cortazar" href="http://es.wikipedia.org/wiki/Julio_Cort%C3%A1zar" id="oa53">Cortazar</a> y <a title="Farenheit 451" href="http://es.wikipedia.org/wiki/Fahrenheit_451" id="jk7l">Farenheit 451</a> de <a title="Bradbury" href="http://es.wikipedia.org/wiki/Ray_Bradbury" id="tucz">Bradbury</a>: me aburrieron terriblemente y los deje, pero años después volví a leerlos y me encantaron. <br><br>Quien sabe quizás me pase lo mismo con los cuentos de El Aleph.<br><br><span style="color: rgb(204, 0, 0);">* Cuidado las paginas de la Wikipedia tienen algunos </span><a style="color: rgb(204, 0, 0);" title="spoilers" href="http://es.wikipedia.org/wiki/Spoiler" id="ljqh">spoilers</a><span style="color: rgb(204, 0, 0);">... después no digan que no se los advertí.</span><br>Diego Fernándezhttp://www.blogger.com/profile/11282484801526539091noreply@blogger.com0tag:blogger.com,1999:blog-1951692453295043766.post-1026553787264683022009-12-10T14:21:00.001-03:002009-12-10T14:40:34.061-03:00Test driven development... algunas lecciones aprendidas<div>En este post quería compartirles algunas cuestiones sobre Test Driven Development (TDD) que fui aprendiendo de los proyectos y personas con las que trabaje. <br><h2>TDD != Testing en busca de bugs<br></h2><p>Es muy común confundir TDD con testing: al menos comparten la palabra "test" en el nombre.<br></p><p>La diferencia esta en que uno generalmente usa la palabra "testing" para referirse a la búsqueda de bugs. Mientras que en TDD la intención es otra: uno busca agilizar el diseño, facilitando la incorporación de cambios.<br></p><p>A grandes rasgos TDD consiste en expresar con un programa las "expectativas" sobre lo que se va a desarrollar.<br></p><br></div><div>Supongamos, por ejemplo, que estamos aprendiendo C con <a title="libro de Kernighan y Ritchie" href="http://en.wikipedia.org/wiki/The_C_Programming_Language_%28book%29" id="w41v">libro de Kernighan y Ritchie</a> y su ya <a title="famoso "Hello World"" href="http://en.wikipedia.org/wiki/Hello_world_program" id="l00i">famoso "Hello World"</a>, así que escribimos:<br><br><div style="margin-left: 40px;"><span style="font-family: Courier New;">#include <stdio.h></span><br style="font-family: Courier New;"><span style="font-family: Courier New;">int main() {</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> printf("Hello World\n")</span><br><span style="font-family: Courier New;"> return 0;</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">}</span><br style="font-family: Courier New;"></div><br>Compilamos... <br><br><div style="margin-left: 40px;"><span style="font-family: Courier New;">$ cc hello.c -o hello</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">hello.c: In function ‘main’:</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">hello.c:5: error: expected ‘;’ before ‘}’ token</span><br></div><br>Arreglamos código, compilamos y ejecutamos, hasta que vemos en la pantalla el resultado esperado:<br><br><div style="margin-left: 40px;"><span style="font-family: Courier New;">Hello World</span><br></div><br>La diferencia usando TDD es que primero escribimos un programa para verificar el resultado esperado (algo que comúnmente hacemos de forma manual):<br><br><div style="margin-left: 40px;"><span style="font-family: Courier New;">ejecutarPrograma();</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">verificar(salidaDePrograma, "Hello World");</span><br></div><div style="margin-left: 40px;"></div></div><div><br>Nuestro "test", no intenta buscar bugs probando diferentes casos, o casos "borde" que puedan ser problemáticos. <br>Si no que simplemente es una forma de establecer que es lo que queremos hacer. <br><br>Esta diferencia es sutil pero tiene muchas implicancias en la forma de trabajar:<br><ul><li>No es necesario pensar casos de test complejos, solo basta con pensar que es lo que se quiere hacer.</li><li>Con TDD el ciclo de <i>escribir código - compilar</i>, se transforma en: <i>escribir tests/código - compilar/ejecutar tests</i>. <br>Es decir, crear/ejecutar tests debe ser parte común del desarrollo. Si los tests tardan en ejecutarse se transforman en una carga.</li></ul><br>En mi caso los "tests" usados de esta forma se convierten en una especie de "TODO list". En efecto muchas veces siento que si no tengo planteado un "test" (aunque sea en mi cabeza) no sé por donde empezar.<br><br><h2>¿Vale la pena dedicar tiempo a escribir tests?</h2><br>El ejemplo de "Hello World" muestra una cuestión evidente: programar el test puede ser mucho más difícil que hacer el programa a testear. <br>Cabe preguntarse si es siempre
así, y si el trabajo adicional vale la pena.<br><br>En general no siempre es tan difícil escribir tests, y hay factores que influyen en la dificultad:<br><ul><li><i>Componentes y frameworks "acoplados" a sistemas externos.</i><br>Por ejemplo, si quisieran escribir el test del "Hello World" en C deberían capturar el STDOUT del programa. Suele ser fácil reemplazar la salida standard, aunque si el stream fuese parametrizable sería mucho más fácil.</li><li><i>Frameworks que no proveen interfaces separadas de a implementación.</i><br>Siguiendo con el "Hello World", supongamos que podemos parametrizar el stream de salida, sería ideal poder capturar la salida en un String. <br>Si el "Stream" que nos brinda el lenguaje no permite hacerlo, podemos hacer otra implementación que cumpla la misma interfaz y capture el String. Pero si "Stream" es una clase cerrada, estamos en problemas.<br></li><li><i>Diseños que no cumplen con el principio de <a title="Single Responsibility" href="http://en.wikipedia.org/wiki/Single_responsibility_principle" id="d.g4">Single Responsibility</a>. </i><br>Si la "responsabilidad" de lo que queremos testear es acotada, entonces el test también va a ser más corto y fácil de escribir.<br></li><li><i>Dependencias globales mutables (como Singletons que no son "singletons"). </i><br>En <a title="un post anterior" href="http://diegacho.blogspot.com/2009/08/side-effects.html" id="uo8x">un post anterior</a> ya hable sobre los problemas del estado global... así que voy a evitar la tentación de volver a comentarlos :)<br></li><li><i>Tests con grandes conjuntos de datos. </i><br>Complican la mantención del test y por lo general también hacen que la ejecución sea más lenta.</li><li><i>El lenguaje/tecnología a usar influyen en la forma de trabajo. </i><br>Por ejemplo en Smalltalk es muy común desarrollar mientras se ejecuta el test (completando lo que falta implementar en el debugger), ya que entorno permite modificar clases sin "parar el mundo". En Java este tipo de prácticas no es posible. <br>En otro extremo el lenguaje C++ no cuenta con reflection out-of-the-box, y por lo tanto escribir tests es mucho más trabajoso (hay que indicarle al framework explicitamente que tests ejecutar).<br>Es conveniente tener en cuenta la tecnología que se usa, por ejemplo en C++ intentería tener tests más grandes para evitar el "overhead" que implica crear tests nuevos. En Smalltalk, Java u otros lenguajes el entorno ayuda a que este problema no exista.<br></li></ul><br>Desde un punto de vista de management, quizás la cuestión esta en cual es el "retorno de inversión" de lidiar con todo esto. Para mi el valor esta en que:<br><br><ul><li>Al tener tests automatizados uno tiene más confianza a la hora de hacer refactorings o cambios en el diseño. <br>Esto puedo afirmarlo con mi experiencia: trabaje en un sistema financiero donde teníamos una gran cantidad de tests (si no recuerdo mal, más de 10.000).<br>Tuve que hacer un rediseño importante de una parte "core" del sistema, hice los cambios y emepecé a ver los tests que fallaban. <br>Los tests que entendí, los arregle. Los que no, los consulte con sus autores y los arreglamos en conjunto. Todo este trabajo hubiese llevado muchísimo más tiempo sin tests automatizados.</li></ul><ul><li>Ayuda a tener un diseño más abierto a los cambios<br>La razón es simple: cuanto más acoplado esta un objeto al uso de sistemas o frameworks externos, más difícil es testearlo. Por lo tanto con TDD uno tiende a diseñar de forma tal de disminuir este acoplamiento. <br>Lo mismo ocurre cuando los objetos tienen dependencias globales mutables: se vuelven dificiles de testear por que un test cambia el estado global, haciendo que otro test falle. Por lo tanto uno tiende a evitar este tipo de globales.<br>A la larga las consecuencias de usar TDD son beneficiosas para el diseño.<br></li></ul><br><h2>No todo es color verde...</h2>Como mencione antes: la intención de TDD es facilitar cambios y ayudar en el diseño. No encontrar bugs. <br><br>Por lo tanto aunque la aplicación pase los tests de unidad, puede contener errores funcionales (casos de test mal planteados), bugs por casos "especiales" y problemas de UI.<br><br>Volviendo al ejemplo de este sistema en el que trabaje con más de 10.000 tests de unidad:<br><div style="margin-left: 40px;">Al principio no teniamos un equipo de QA, los tests automáticos pasaban y algunos de nosotros probamos la aplicación durante el desarrollo. <br><br>Llego el día "D": empaquetamos todo para producción y llevamos el software al cliente.<br><br>A las horas recibimos un llamado: un botón de la UI permanecía deshabilitado y el cliente no podía acceder a una funcionalidad del programa (funcionalidad que estaba desarrollada, pero inaccesible). Asi que tuvimos que corregir el problema y hacer un nuevo deploy.<br></div><br>Los tests de unidad no reemplazan a un equipo de QA. No es la intención de TDD reemplazar las prácticas comunes de testing y verificación de calidad.<br><br><h2>La diferencia esta en los detalles</h2>Supongamos los convencí de las "bondades" de TDD. Asi que empiezan a practicarlo en un proyecto.<br><br>Al tiempo hay una alta probabilidad de que empiece a pasar lo siguiente. Algunos tests fallan pero arreglarlos es un problema: los tests se volvieron enormes e inmantenibles. Solución rápida: los tests que fallan se ignoran. Un par de meses despúes los tests que fallan se siguen ignorando, y se pierde la ventaja de TDD: si necesitamos hacer un cambio de diseño no tenemos forma de saber que rompimos.<br><br>¿Por que se llega a este punto? <br>Principalmente es una cuestión de la costumbre del equipo en hacer y mantener los tests. <br>Pero además hay un montón de detalles que con el tiempo se acumulan cual bola de nieve e influyen en que los tests se vuelvan inmantenibles. Algunos "detalles" a tener en cuenta:<br><br><h3>No depender de bases de datos externas</h3>Hacer que un test de unidad requiera de una base de datos es problemático por que:<br><ul><li>El entorno de desarrollo es dificil de configurar: cada desarrollador tiene que configurar los drivers y conexiones para poder empezar a trabajar con los tests.</li><li>Si se usa una base compartida: un cambio hace que los tests fallen para el resto de los desarrolladores. Para hacer TDD los tests se deben correr todo el tiempo, asi que tener tests que fallen por cambios de otros es inadminsible.</li><li>Es más trabajoso para usar un servidor de integración continua: hay que asegurarse de que el servidor de integración use correctamente la base de datos de prueba.</li><li>La ejecución de los tests suele ser más lenta, asi que correr todos los tests no es algo que se haga muy seguido.<br></li></ul><br>En el caso de que un test necesite una base de datos es necesario distinguir:<br><ul><li>Que es lo que se esta testeando: ¿Quiero realmente testear el acceso a la base de datos?</li><li>¿Puedo reemplazar el acceso a la base de datos, por una implementación que "simule" la respuesta y no acceda a una base de datos real?</li></ul><br>Muchas veces uno quiere testear la implementación a la base de datos, por ejemplo se usa Hibernate para responder algunas consultas y cambiar la implementación por una que simule la base no aporta nada. <br>En esos casos lo mejor es usar una base en memoria (como <a title="HSQLDB" href="http://hsqldb.org/" id="fhz0">HSQLDB</a>) y levantarla durante el test.<br><br>HSQLDB es liviano, soporta Hibernate y toda la sintaxis de SQL. El unico punto en contra es que si uno quiere "ver" como se guardan las cosas en la base hay que frenar el test para no bajar el servidor.<br><br>El siguiente es un pequeño ejemplo de como usar HSQLDB en un test:<br><br><div style="margin-left: 40px;"><font size="2"><span style="font-family: Courier New;">public class TestDatabase {</span></font><br style="font-family: Courier New;"><font size="2"><span style="font-family: Courier New;"> public static final String DEFAULT_DATABASE_NAME = "test";</span></font><br style="font-family: Courier New;"><font size="2"><span style="font-family: Courier New;"> public static final int DEFAULT_SERVER_PORT = 9001;</span></font><br style="font-family: Courier New;"><br style="font-family: Courier New;"><font size="2"><span style="font-family: Courier New;"> private Server hsqldbServer;</span></font><br style="font-family: Courier New;"><font size="2"><span style="font-family: Courier New;"> private HsqlProperties hsqldbProperties;</span></font><br style="font-family: Courier New;"><font size="2"><span style="font-family: Courier New;"> private String jdbcConnectionUrl;</span></font><br style="font-family: Courier New;"><font size="2"><span style="font-family: Courier New;"> </span></font><br style="font-family: Courier New;"><font size="2"><span style="font-family: Courier New;"> public TestDatabase(String databaseName, int serverPort) {</span></font><br style="font-family: Courier New;"><font size="2"><span style="font-family: Courier New;"> hsqldbProperties = new HsqlProperties();</span></font><br style="font-family: Courier New;"><font size="2"><span style="font-family: Courier New;"> jdbcConnectionUrl = "jdbc:hsqldb:hsql://localhost:" + serverPort + "/" + databaseName;</span></font><br style="font-family: Courier New;"><font size="2"><span style="font-family: Courier New;"> hsqldbProperties.setProperty("server.port", serverPort);</span></font><br style="font-family: Courier New;"><font size="2"><span style="font-family: Courier New;"> hsqldbProperties.setProperty("server.database.0", databaseName);</span></font><br style="font-family: Courier New;"><font size="2"><span style="font-family: Courier New;"> hsqldbProperties.setProperty("server.dbname.0", databaseName);</span></font><br style="font-family: Courier New;"><font size="2"><span style="font-family: Courier New;"> }</span></font><br style="font-family: Courier New;"><br style="font-family: Courier New;"><font size="2"><span style="font-family: Courier New;"> public static TestDatabase startWithDefaultConfiguration() {</span></font><br style="font-family: Courier New;"><font size="2"><span style="font-family: Courier New;"> return new TestDatabase(DEFAULT_DATABASE_NAME, DEFAULT_SERVER_PORT).start();</span></font><br style="font-family: Courier New;"><font size="2"><span style="font-family: Courier New;"> }</span></font><br style="font-family: Courier New;"><br style="font-family: Courier New;"><font size="2"><span style="font-family: Courier New;"> private TestDatabase start() {</span></font><br style="font-family: Courier New;"><font size="2"><span style="font-family: Courier New;"> hsqldbServer = new Server();</span></font><br style="font-family: Courier New;"><font size="2"><span style="font-family: Courier New;"> hsqldbServer.setProperties(hsqldbProperties);</span></font><br style="font-family: Courier New;"><font size="2"><span style="font-family: Courier New;"> hsqldbServer.start();</span></font><br style="font-family: Courier New;"><font size="2"><span style="font-family: Courier New;"> return this;</span></font><br style="font-family: Courier New;"><font size="2"><span style="font-family: Courier New;"> }</span></font><br style="font-family: Courier New;"><br style="font-family: Courier New;"><font size="2"><span style="font-family: Courier New;"> public void shutdown() {</span></font><br style="font-family: Courier New;"><font size="2"><span style="font-family: Courier New;"> if (hsqldbServer == null) return;</span></font><br style="font-family: Courier New;"><font size="2"><span style="font-family: Courier New;"> hsqldbServer.shutdown();</span></font><br style="font-family: Courier New;"><font size="2"><span style="font-family: Courier New;"> }</span></font><br style="font-family: Courier New;"><font size="2"><span style="font-family: Courier New;">}</span></font><br></div><br><div style="margin-left: 40px;"><span style="font-family: Courier New;">public class AccesoALaBaseTest {</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> private static TestDatabase database;<br><br style="font-family: Courier New;"></span><span style="font-family: Courier New;"> @BeforeClass</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> public static void startTestDatabase() {</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> database = TestDatabase.startWithDefaultConfiguration();</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> }</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> </span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> @AfterClass</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> public static void shutdownHsqldbServer() {</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> database.shutdown();</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> }</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">}</span><br></div><br><h3>No usar herencia para compartir instancias de prueba</h3>Usar herencia para compartir instancias entre tests es un error muy común.<br><br>Supongamos que estamos testeando la implementación de <i>CajaDeAhorro</i>. Para crear una instancia de la caja de ahorros, necesitamos una instancia de <i>Cliente</i>, que a su vez necesita una instancia de <i>Contrato</i>, que a su vez necesita una instancia de... bueno se hacen a la idea: crear todas estas instancias es trabajoso, nos tomamos el trabajo :(<br><br>Despúes queremos testear <i>CuentaCorriente</i>, y queremos reusar las instancias que creamos para el test de <i>CajaDeAhorro</i>. <br>Muchas veces se suele crear una superclase para el test, supongamos <i>AbstractTest</i>, donde se colocan estas instancias que necesitamos compartir. <br>Los desarrolladores empiezan a heredar todos los tests de <i>AbstractTest</i>, y van "subiendo" instancias que quieren compartir entre tests.<br><br>¿Se ve el problema?<br><i>AbstractTest </i>termina siendo una gran bolsa de gatos. Cuando se quiere refactorizar <i>AbstractTest </i>hay otro problema: es engorroso buscar las referencias a cada variable usada en las subclases. Por lo general este refactoring implica mucho trabajo y nadie lo hace. <i>AbstractTest </i>sobrevive a varias versiones transformandose en una inmensa <a title="bola de lodo" href="http://www.laputan.org/mud/" id="jd63">bola de lodo</a>.<br><br>La solución:<br>No usar herencia para compartir instancias entre los tests (usar herencia para re usar código es mala idea, en este caso es malisima). <br>Es preferible crear una clase, por ejemplo <i>ClienteTestResource </i>(suelo usar el sufijo "TestResource" para esas clases) que brinda instancias de <i>Cliente </i>para los tests.<br><br>Nada evita que <i>ClienteTestResource </i>no se convierta tambien en una bolsa de gatos, pero la cuestión es más controlada: uno puede crear clases distintas para agrupar recursos necesarios en los tests. Y a diferencia de <i>AbstractTest</i>, los desarrolladores solo usan <i>ClienteTestResource </i>cuando lo necesitan.<br><br><h3>Evitar código redundante</h3>Otro problema común es la actitud de: "hago copy & paste, total es un test!".<br><br>Esto es perjudicial: <b>los tests tambien hay que mantenerlos</b>. Si hay que hacer un "copy & paste" para crear instancias que se necesitan en el test... usar un "TestResource".<br><br>Si hay que implementar un método en común para facilitar el testing: ¿Entonces por que no implementarlo en el modelo? Por ejemplo, si estoy testeando el balance de una cuenta y el codigo del test es algo asi:<br><br><div style="margin-left: 40px;"><span style="font-family: Courier New;">cuenta.balanceAl(crearFecha("01/01/2009"));</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">...</span><br style="font-family: Courier New;"><br style="font-family: Courier New;"><span style="font-family: Courier New;">private function Date crearFecha(String s) {</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> SimpleDateFormat sdf .....</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> return sdf.parse(s);</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">}</span><br></div><br>Y empiezo a copiar "crearFecha" en varios tests, la alternativa es dar una interfaz amigable en <i>Cuenta </i>(la otra alternativa es tener una interfaz más amigable para construir instancias de Date en general, un buen ejemplo de esto en Smalltalk es el framework <a title="Chaltén" href="http://www.squeaksource.com/Chalten.html" id="yjp0">Chaltén</a>, que pemite expresar fechas como: "Jaunary first, 2009" donde Jaunary es un objeto first y , son mensajes... asi que la expresión es basicamente da una fecha) que permita escribir:<br><br style="font-family: Courier New;"><div style="margin-left: 40px;"><span style="font-family: Courier New;">cuenta.balanceAl("01/01/2009");</span><br></div><br>Eso no quita que el metodo balanceAl(Date) no tenga que existir. El formateo de fechas depende del Locale y no debería usarse con un Locale implicito en la aplicación. Sin embargo es conveniente pensar en la facilidad de uso de las interfaces que uno provee: las interfaces de los objetos son como las interfaces graficas.<br><br>Tener interfaces que permitan expresar las cosas de forma natural, usando defaults apropiados y simplificando ciertas cuestiones, ayuda muchisimo a la hora de escribir tests.<br><br>La clave es pensar que cuando uno programa esta construyendo un lenguaje, y esto abarca tambien a los tests. No es lo mismo escribir:<br><br><div style="margin-left: 40px;"><span style="font-family: Courier New;">assertThat(coleccion, hasItem("hola"));</span><br></div><br>Que escribir:<br><br><div style="margin-left: 40px;"><span style="font-family: Courier New;">boolean found = false;</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">for (String s : coleccion) {</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> if (s.equals("hola")) found = true;</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">}</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">assertTrue(found);</span><br></div><br>(Nota: sé que existe coleccion.contains("hola") pero quería explicitar la fealdad de esta alternativa ;) )<h3>Un test por "observación"</h3>Supongamos que para una <i>CajaDeAhorro </i>queremos testear que:<br><ul><li>Los depositos incrementan el balance.</li><li>Las extracciones decrementan el balance.</li><li>No se puede hacer una extracción si no hay fondos.</li></ul><br>Uno puede estar tentado a testear todo esto junto:<br><br><div style="margin-left: 40px;"><span style="font-family: Courier New;">@Test public void depositoYExtraccion() {</span><br style="font-family: Courier New;"></div><div style="margin-left: 40px;"><div style="margin-left: 40px;"><span style="font-family: Courier New;">assertThat(cuenta.balance(), is(monto));</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">cuenta.depositar(monto);</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">assertThat(cuenta.balance(), is(monto * 2));</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">cuenta.extraer(cuenta.balance());</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">assertThat(cuenta.balance(), is(0));</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">try {</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> cuenta.extraer(monto);</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> fail();</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">} catch (NoHayFondosException e) {}</span><br style="font-family: Courier New;"></div></div><div style="margin-left: 40px;"><span style="font-family: Courier New;">}</span><br></div><br>Pero tiene algunos problemas:<br><ul><li>Si el test falla es necesario debuggear para saber si el problema esta en depositar, extraer o balance.</li><li>El ejemplo es chico, pero en tests más grandes los errores tienden ser más dificiles de ver.</li></ul><br>En comparación esta solución es mejor:<br><br><div style="margin-left: 40px;"><span style="font-family: Courier New;">@Test public void losDepositosIncrementanElBalance() {</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> assertThat(cuenta.balance(), is(0));</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">
cuenta.depositar(monto);</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> assertThat(cuenta.balance(), is(monto));</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">}</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">@Test public void lasExtraccionesDecrementanElBalance() {</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> assertThat(cuenta.balance(), is(not(0)));</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">
cuenta.extraer(cuenta.balance());</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> assertThat(cuenta.balance(), is(0));</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">
}</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">@Test(expected=NoHayFondosException.class) </span><br style="font-family: Courier New;"><span style="font-family: Courier New;">public void noSePuedeHacerUnaExtraccionSiNoHayFondos() {</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">
assertThat(cuenta.balance(), is(not(0)));</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">
cuenta.extraer(cuenta.balance() * 2);</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">
}</span><br style="font-family: Courier New;"></div>
<br>Parece más largo pero tiene varias ventajas:<br><ul><li>Es más facil ver que esta fallando.</li><li>Si un test falla es más facil de corregir.</li><li>Tiene (para mi) una implicación psicologica: ir haciendo pequeños tests que van pasando, en general me ayuda a tener un mejor ritmo de trabajo. Incluso entre pequeños test a veces se me ocurren rediseños que no habia pensado originalmente.</li></ul><br><h3>Evitar tests no deterministicos</h3>Cuando el resultado esperado depende de un valor del "entorno", por ejemplo: la hora actual o un número random. Es común terminar con tests no deterministicos: a veces pasan y otras veces no.<br><br>Por ejemplo supongamos que estamos testeando una aplicación que genera registros de auditoria con la hora actual:<br><br><div style="margin-left: 40px;"><span style="font-family: Courier New;">registroEsperado = new RegistroAuditoria(nuevoTimestamp, etc, etc, etc);</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">sistema.ejecutarMetodoQueGeneraAuditoria();</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">assertThat(sistema.logDeAuditoria(), hasItem(registroEsperado));</span><br></div><br>El problema es que si la comparación de <i>RegistroAuditoria </i>involucra comparar el "time stamp", entonces el test pasa o falla según la resolución de la hora y si hubo pausas en el medio de la ejecución (por ejemplo se ejecutó el GC). <br><br>Una forma de evitar este tipo de cosas es "fijar" estos valores. Por ejemplo podemos tener una interfaz <i>Clock </i>que provee la hora:<br><br><div style="margin-left: 40px;"><span style="font-family: Courier New;">Clock fixedClock = new FixedClock(nuevoTimestamp);</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">sistema = new Sistema(fixedClock);</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">registroEsperado = new RegistroAuditoria(nuevoTimestamp, etc, etc, etc);</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">
sistema.ejecutarMetodoQueGeneraAuditoria();</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">
assertThat(sistema.logDeAuditoria(), hasItem(registroEsperado));</span><br style="font-family: Courier New;"></div><br>Ya no hay más problemas de indeterminismo :)<br><br><h3>Evitar delays</h3>Muchas veces por razones de concurrencia, se termina agregando al test un "delay" (por ejemplo se ejecuta un thread y quiero esperar a que el thread actualize un valor que despues voy a verificar).<br><br>Este tipo de test tiene dos problemas:<br><ul><li>El delay hace que los tests corran más lento.</li><li>Por lo general resultan en tests no-deterministicos por que dependen de como la plataforma maneja la ejecución de los threads.</li></ul><br>La alternativa es investigar un poco más lo que se esta testeando. Si es necesario tener el thread separado en el test conviene usar un semanforo (o monitor en Java) para hacer un wait hasta que se modifique el valor.<br><br>Lamentablemente no tengo ningun ejemplo a mano para mostrar en el blog. <br>Lo he hecho tanto en Smalltalk y en Java, y quizas lo unico que puedo agregar es que las cosas concurrentes son extremandamente dificiles de testear. <br>Algunas veces una solución intermedia es usar "yield" para que la VM ejecute otros procesos, esto salva el delay pero puede generar tambien tests no-deterministicos... la mejor opción sigue siendo usar algun tipo de semaforo.<br><br>De todas formas vale la pena hacer la "investigación" para evitar el delay: uno termina aprendiendo muchas cosas de concurrencia :), y tener un delay de un 1seg en varios tests es muy molesto.<br><br><h3>Usar mock objects con cuidado</h3>Un <a title="mock object" href="http://en.wikipedia.org/wiki/Mock_object" id="er2l">mock object</a> es simplemente una implementación "de mentira" que se usa para reemplazar en un test a una implementación real (esta es al menos mi definición de mock object).<br><br>En el ejemplo anterior <i>FixedClock </i>es una implementación de mentira de <i>Clock</i>, que nos facilita el testing.<br><br>Existen <a title="frameworks de mock objects" href="http://stackoverflow.com/questions/22697/whats-the-best-mock-framework-for-java" id="mi1-">frameworks de mock objects</a> que por lo general:<br><ul><li>Facilitan la implementación de mock objects usando meta-programación.</li><li>Permiten hacer tests de caja blanca, pudiendo validar si se ejecuto o no un método del mock object.</li></ul><br>En mi experiecia estos frameworks terminan derivando en tests dificiles de mantener:<br><ul><li>Los tests de "caja blanca" suelen ser una mala idea, al primer refactoring fallan y se convierten en un "dolor" de mantener. En esos casos es mejor re-plantearse el caso de test (y recordar cual es la intención de los tests en TDD).</li><li>En general es mucho más facil tener una interfaz y hacer la implementación de mentira para el test que usar un framework de mock objects.</li><li>Cuando no se generan dependencias adicionales (explicación a continuación) y no hay problemas como en el ejemplo de <i>FixedClock</i>, es más facil usar objetos reales que mock objects (usar un "TestResource" puede ayudar a escribir menos codigo en estos casos).</li><li>En lenguajes dinamicos como Smalltalk es muchisimo mas facil crear mock objects, pero tambien tienen problemas: si se usan muchos mock objects y se hace un refactoring, es probable que los test compilen y pasen... aunque deberían haber fallado.</li></ul><br>Un pequeño hint: usar mock objects no es necesariamente malo, pero necesitar de mucha logica en un mock object es signo de que algo esta mal. A veces no se necesita un mock object si no una implementación más sencilla de la "interfaz" que queremos simular. <br><h3>Ser cuidadoso con las dependencias de los tests</h3>Ya estoy llegando al final de este enorme post.<br><br>El tema de las dependencias de tests requiere un poco más de explicación, asi que voy a ser breve:<br><br>Muchas veces uno expresa los tests a más alto nivel por ejemplo:<br><br><div style="margin-left: 40px;"><i>"El usuario agrega items al carrito de compras. Procede al checkout, donde obtiene una factura de los items que compró"<br></i></div><br>Este test abarca toda una historia de usuario y a veces es bueno automatizarlo.<br><br>Sin embargo hay que tener en cuenta que este tipo de test no solo es más largo, si no que posiblemente dependa de otras implementaciones: por ejemplo el carrito de compras se implementa en un proyecto, la generación de factura en otro y quien usa ambas implementaciones (llamemosle <i>Cajero</i>) usa interfaces asi que el proyecto donde esta el <i>Cajero </i>no depende de una implementacion particular de carrito de compra o el generador de factura.<br><br>¿Donde colocamos el test?<br>Si lo hacemos en el proyecto donde esta <i>Cajero</i>, y no usamos mock objects, generamos dependencias adicionales que no son necesarias (o quizas dependencias circulares que son problematicas). Y si usamos mock objects para todo, el test se vuelve más dificil de mantener.<br></div><br>Lo mejor en estos casos es ver el test a otro nivel. Si quieren pueden llamarle "test de integración" (siguiendo una convención que usabamos en Mercap, prefiero llamarles "user story test" por que abarcan una historia de usuario, y reservar el nombre "test de integración" para tests que verifican la integración con sistemas externos).<br><br>Es conveniente que este tipo de test este en un proyecto separado. De esta forma se evitan dependencias circulares, y es más facil compartir información entre dintintos "user story tests" (usando de "TestResources").<br><br>Además este tipo de test suele ser más lento, y por lo general no se ejecuta con la misma frecuencia que los tests de unidad.<br><br>El problema de este tipo de test es que son más grandes, tienen más dependencias y por lo tanto más dificiles de mantener. <br><br>Pero cuando las fechas de entrega apremian, a veces no hay tiempo para estar haciendo muchos tests a nivel unitario. En esos casos con tests de este estilo se puede testear la implementación manteniendo un estilo TDD (uno escribe el "user story test" antes de empezar), sin necesidad de crear un test por cada una de las clases que se usan "internamente". En este caso las herramientas de covertura pueden ayudar a ver que se cubre con el "user story test" y que no. <br><br>Si bien esta es una alternativa que complementa a los tests de unidad, no los reemplaza: los tests de unidad son una buena forma de trabajar a "nivel micro", pueden ejecutarse todo el tiempo durante el desarrollo y son más faciles de mantener.<br><br>Espero no haberlos dormido con este post enorme, trate de resumir un poco las "lecciones aprendidas" con TDD. <br>Hasta la proxima :)<br><br>Diego Fernándezhttp://www.blogger.com/profile/11282484801526539091noreply@blogger.com1tag:blogger.com,1999:blog-1951692453295043766.post-27424540516106231032009-08-12T20:42:00.002-03:002009-08-12T21:16:42.697-03:00Side effectsEl <a href="http://diegacho.blogspot.com/2009/07/estados.html">post anterior</a> estuvo dedicado a la representación de "estados" en el dominio, donde mencione al pasar que el código del estilo: "objeto.getEstado()/objeto.setEstado(X)" tiene además otras complejidades, producto de algo que los programadores de <a href="http://en.wikipedia.org/wiki/Functional_languages">lenguajes funcionales</a> odian bastante: <a href="http://en.wikipedia.org/wiki/Side_effect_%28computer_science%29">side effects</a>.<br><br>Para los que se desayunan con el término voy a contarles de que se trata. Supongamos el siguiente programa:<br><br><span style="font-family: Courier New;">a := 10</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">b := a + 10</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">if (b = a + 10) then retornar "OK" else retornar "FALLA"</span><br><br>Sabemos de ante mano que el programa retorna "OK", asi que podemos simplificar el código y ahorrarnos muchas vueltas (incluso el compilador podría realizar estas simplificaciones por nosotros). Ahora añadimos un procedimiento "inofensivo":<br><br><span style="font-family: Courier New;">a := 10</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">b := a + 10</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">procedimientoRaro()</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">if (b = a + 10) then retornar "OK" else retornar "FALLA"</span><br><br>¿Seguimos sabiendo que el programa retorna siempre "OK"?<br>Antes de decir si, voy a poner una condición tramposa: <br>Dado que este es un programa en pseudo-código y no sabemos como se evalua nuestro supuesto lenguaje, supongamos que la variable "a" se puede acceder y modificar desde "procedimientoRaro()". <br><br>Ahora si volvamos a la pregunta: ¿El programa retorna siempre "OK"? <br>No lo sabemos :-(. <br>Tendríamos que examinar el "procedimientoRaro()" (si es que tenemos el código) un lindo procedimiento con más de 200 lineas de código y millones de ifs/fors anidados.<br><br>Sobre la "modularidad" del ejemplo podríamos decir que la variable "a" es una global y "procedimientoRaro" no debería usar globales, etc, etc. Asi que vamos a cambiarlo:<br><br><span style="font-family: Courier New;">a := 10</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">b := a + 10</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">procedimientoRaro(a)</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">if (b = a + 10) then retornar "OK" else retornar "FALLA"</span><br><br>¿Seguimos sabiendo que el programa retorna siempre "OK"? <br>Quizás no, el problema es si "procedimientoRaro" puede modificar o no el valor de "a", y si nuestro lenguaje copia el argumento o lo pasa por referencia.<br><br>Podemos seguir con innumerables ejemplos, la cuestión es que en nuestro <a href="http://en.wikipedia.org/wiki/Imperative_programming">lenguaje imperativo</a> "procedimientoRaro" puede tener <i>side effects</i>: afecta estados compartidos con el resto del programa.<br>La raíz de los problemas reside en la asignación. Al incorporar la capacidad de asignar valores a la variable "a", incorporamos implícitamente la noción de estado de ejecución. <br>Es decir la verificación "b = a + 10" ya no depende de la solo de la expresión, si no que depende además del estado. (hay una explicación muy buena sobre asignación y estado en el <a href="http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-20.html#%_sec_3.1">capitulo 3</a> del libro <a href="http://mitpress.mit.edu/sicp/full-text/book/book.html">Structure and Interpretation of Programs</a>).<br><br> <h2>“Todo esto parece muy teórico. En el trabajo diario ¿De qué me sirve?”</h2>Supongamos que estamos trabajando en un sistema que se encarga de registrar la reserva de salas para una empresa de capacitación:<br><ul><li><i>CalendarioDeSalas</i>: Lleva un control de la reserva de salas. Una sala no puede reservarse en un intervalo de tiempo que ya este en uso.</li><li><i>IntervaloDeTiempo</i>: Representa un rango de fecha-hora.</li></ul><br><img src="http://yuml.me/diagram/class/%5BCalendarioDeSalas%7Creservar%28Sala%20IntervaloDeTiempo%29%5D,%5BIntervaloDeTiempo%7CsetDesde%28x%29;%20setHasta%28x%29;%20getDesde%28%29;%20getHasta%28%29%5D"><br><br>Con estas clases podríamos escribir un código así:<br><br><span style="font-family: Courier New; color: rgb(56, 118, 29);">// reserva la sala del día 16 al 17</span><br style="font-family: Courier New; color: rgb(56, 118, 29);"><span style="font-family: Courier New;"><span style="color: rgb(56, 118, 29);">// el calendario verifica la disponibilidad</span> </span><br style="font-family: Courier New;"><span style="font-family: Courier New;">intervaloDeTiempo = new IntervaloDeTiempo();</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">intervaloDeTiempo.setDesde("16/07/2009");</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">intervaloDeTiempo.setHasta("17/07/2009");</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">calendario.reservar(unaSala, intervaloDeTiempo); </span><br style="font-family: Courier New;"><br style="font-family: Courier New;"> <span style="font-family: Courier New; color: rgb(56, 118, 29);">// reserva la sala del día 20 al 21</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">otroIntervaloDeTiempo = new IntervaloDeTiempo();</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">otroIntervaloDeTiempo.setDesde("20/07/2009");</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">otroIntervaloDeTiempo.setHasta("21/07/2009");</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">calendario.reservar(unaSala, otroIntervaloDeTiempo);</span><br style="font-family: Courier New;"><br style="font-family: Courier New;"> <span style="font-family: Courier New; color: rgb(56, 118, 29);">// cambia la fecha de la primer reserva..</span><br style="font-family: Courier New; color: rgb(56, 118, 29);"><span style="font-family: Courier New; color: rgb(56, 118, 29);">// OOPS! el calendario ni se entera!</span><br style="font-family: Courier New; color: rgb(56, 118, 29);"><span style="font-family: Courier New; color: rgb(56, 118, 29);">// este cambio rompe con la especificación del calendario</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">intervaloDeTiempo.setDesde("20/07/2009");</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">intervaloDeTiempo.setHasta("21/07/2009");</span><br><br> <h2>“Pero si esto es un problema: ¿Por qué no me encuentro con estos errores en mi mega sistema J2EE/Spring/Hibernate/etc/etc…?”</h2>El problema del ejemplo puede aliviarse superficialmente si el calendario se encarga de copiar el intervalo antes de registrarlo. <br>Esto es básicamente lo que sucede con todos los mapeos Objeto-Relacional: lo valores se copian a la base de datos y luego las instancias son regeneradas.<br>Pero el problema aunque oculto sigue estando. Basta con agregar un “cache”, o usar la instancia de <i>IntervaloDeTiempo</i> de forma compartida sin darse cuenta (por ejemplo en un widget de UI), para que empiecen a ocurrir bugs inesperados. <br><br>También surgen un montón de complicaciones innecesarias en el código debido a las “validaciones”.<br><br>Supongamos que quiero que en un <i>IntervaloDeTiempo</i> la fecha inicial sea siempre menor a la final. Empiezo a agregar una guarda en “<i>setDesde</i>” otra en “<i>setHasta</i>”, y genero una excepción. <br><br>Pero ahora tengo que tener en cuenta en qué orden establezco los valores.<br>Bueno podemos hacer que cuando uno de los valores es <i>null</i> no se haga el chequeo… ¡HORRIBLE! Complicaciones y más complicaciones.<br><br>Simplifiquemos un poco, hacemos un método <i>setIntervalo(desde, hasta)</i>, nos ahorramos problemas de validaciones. Pero seguimos teniendo problemas de otro tipo: para que el calendario funcione correctamente hay que asegurarse que el intervalo de la reserva no se cambie sin conocimiento el calendario.<br><br>Como mencione antes podemos aliviar el problema haciendo que calendario se encargue de copiar la instancia de intervalo que recibe. Pero esta es solo una solución superficial, supongamos que tras varias iteraciones decidimos hacer un refactoring, en lugar de:<br><br><span style="font-family: Courier New;">calendario.reservar(unaSala, intervalo);</span><br><br>Ahora tenemos un objeto que representa la reserva:<br><br><img src="http://yuml.me/diagram/class/%5BReserva%7CsetSala%28Sala%29;setIntervalo%28IntervaloDeTiempo%29;getSala%28%29;getIntervalo%28%29%5D"><br><br><span style="font-family: Courier New;">unaReserva = new Reserva();</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">unaReserva.setSala(unaSala);</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">unaReserva.setIntervalo(intervalo);</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">calendario.registrar(unaReserva);</span><br><br>¿Cómo nos aseguramos que nadie nos cambie el intervalo de la reserva? Tenemos que:<br><ul><li>Copiar la reserva al registrar en el calendario.</li><li>Copiar el intervalo a establecerlo en la reserva.</li><li>Asegurarnos de hacer una nueva copia del intervalo al retornar <i>getIntervalo()</i> en <i>Reserva</i>.</li><li>Asegurarnos de hacer una copia de la reserva al retornarla en el calendario, o decorarla de alguna manera para que nadie cambie la reserva sin que se entere el calendario.</li><li>Inicializar correctamente la sala y el intervalo, y chequear en “registrar” que tengan valores validos.</li><li>¡HORRIBLE! Seguimos teniendo mucha complejidad. </li></ul> <br><h2> “¿Cómo puedo aliviar/evitar estos problemas?”</h2>Evitando side effects, es decir eliminar la asignación (setters) usando objetos inmutables.<br>En el ejemplo es preferible que el valor de las variables de instancia de <i>IntervaloDeTiempo</i> no se puedan cambiar. Es decir que no exista un “<i>setDesde</i>” y “<i>setHasta</i>”.<br><br>Para eso solo hay que definir un constructor adecuado:<br><br><span style="font-family: Courier New;">new IntervaloDeTiempo(desde, hasta);</span><br><br>Lo mismo sucede con <i>Reserva</i>:<br><br><span style="font-family: Courier New;">new Reserva(unaSala, intervalo);</span><br><br><h2>Remando contra la corriente</h2><h2>"¿Hasta que punto se puede diseñar pensando en objetos inmutables?"</h2>Es evidente que hay casos donde conviene que los objetos sean mutables, por ejemplo en entornos donde la configuración puede cambiar dinamicamte (sin ir más lejos un ejemplo de este tipo de cambios dinamicos es el entorno de Smalltalk).<br>En otros casos la necesidad de objetos mutables tiene que ver con cuestiones de performance e interacción con otros frameworks (donde por framework incluyo también a las librerías standard del lenguaje, por ejemplo las colecciones).<br><br>Este ultimo punto es para mi el más molesto para los programadores Java: la gran mayoría de los frameworks Java adoptaron la convención de JavaBeans (<a href="http://diegacho.blogspot.com/2008/02/los-prefijos-set-y-get-en-java-son-un.html">con la que ya me ensañe bastante</a>). El problema es que muchas veces adoptan esa convención de manera totalmente innecesaria, fomentando malas prácticas.<br><br>Por ejemplo, <a href="http://www.springsource.org/about">Spring</a> puede hacer inyección de dependencias usando constructores. Sin embargo el <a href="http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/ch03s04.html#beans-constructor-injection">manual de referencia</a> menciona que la forma preferida es usar "setters", eso significa que la mayoría de los tutoriales usan inyección por setters y por lo tanto la mayoría de los "usuarios" del framework también.<br><br>Pero si uno le presta más atención al por qué, el manual dice: "Setter methods also make objects of that class amenable to reconfiguration or re-injection later", lo que me genera la duda ¿Cuantas veces uno diseña una aplicación con este grado de re-configuración en runtime? Mi punto es: existen tecnologías como JMX que facilitan los cambios en runtime, sin embargo no es el requerimiento común, y si fuese un requerimiento de la aplicación uno tendría que diseñar pensando en la complejidad que pueden generar estos cambios dinámicos.<br><br>La otra justificación para usar setters en Spring es cuando existen dependencias circulares, sin embargo este tipo de dependencias a veces pueden evitarse incorporando en el diseño un tercer objeto que haga de mediador.<br><br> Otros frameworks son peores: al no permitir otro uso que no sea mediante la convención de JavaBeans fuerzan este tipo de prácticas.<br><br>Aún así hay algunas prácticas de diseño que ayudan evitar side effects:<br><ul><li>Mientras sea posible usar objetos inmutables.</li><li>Hacer las validaciones en el constructor, de esta manera si uno tiene una instancia ya se sabe que la misma fue construida correctamente.</li><li>Si el constructor queda enorme (cosa que es muy molesta para manejar), pensar de "dividir" el objeto en conceptos más pequeños. Por ejemplo en el ejemplo anterior la clase <i>Reserva</i> podría recibir el valor desde y hasta del intervalo: <i>Reserva(desde,hasta,sala)</i>. Sin embargo separando el concepto de intervalo es mucho más simple (incluso para las validaciones): <i>Reserva(intervalo,sala)</i>.</li><li>Si la construcción es complicada utilizar un <a href="http://en.wikipedia.org/wiki/Builder_pattern">builder</a>. Lo bueno es que si la interfaz del builder se diseña con cuidado se pueden hacer <a href="http://martinfowler.com/dslwip/InternalOverview.html">DSL internos,</a> logrando un código muy comunicativo.</li><li>Si en el dominio hay distintos "estados", pensar como se representan esos estados y utilizar algunas de las técnicas que mencione en el <a href="http://diegacho.blogspot.com/2009/07/estados.html">post previo</a>. Por ejemplo, si en la UI tenemos que modificar una <i>Reserva</i>, podríamos tener un objeto <i>ReservaBorrador</i> que sea mutable y que actúe como builder de una <i>Reserva</i> inmutable.</li><li>No implementar equals en objetos mutables: es una muy mala práctica. Si no se dan cuenta por que: creen un objeto mutable, calculen equals y hashCode en base a variables de instancia que pueden cambiar, agreguen el objeto a un <i>Set</i>, modifiquen la instancia y vuelvan a probar si la misma esta en el Set.</li><li>Tener cuidado al retornar colecciones en un setter. Para que la colección no sea modificada pueden hacer una copia, o en Java <a href="http://java.sun.com/javase/6/docs/api/java/util/Collections.html#unmodifiableCollection%28java.util.Collection%29">decorarla</a> para evitar modificaciones.</li></ul><br>Estas cuestiones de side effects suelen ser importantes, no solo para el diseño, si no también para la escalabilidad de un sistema. Por eso cada vez más se empiezan a incorporar a los lenguajes orientados a objetos conceptos que vienen de lenguajes funcionales, el lenguaje <a href="http://www.scala-lang.org/">Scala</a> es un buen ejemplo que les recomiendo ver.<br>Diego Fernándezhttp://www.blogger.com/profile/11282484801526539091noreply@blogger.com1