<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-1706575371325125886</id><updated>2012-01-11T12:48:11.359+01:00</updated><category term='formacio'/><category term='hibernate'/><category term='docbook'/><category term='proves'/><category term='peg revision'/><category term='cursos'/><category term='llibres'/><category term='subversion'/><category term='eines'/><category term='humor'/><title type='text'>can Domingo</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>66</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-1050651754641708904</id><published>2011-11-24T22:23:00.001+01:00</published><updated>2011-11-24T22:35:58.306+01:00</updated><title type='text'>Un servidor de calendari</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;Aquests dies he acabat un afegit a l'aplicació en la que estic treballant: un "mini servidor" de calendari.&lt;br /&gt;&lt;br /&gt;La idea és fer accessibles una sèrie d'esdeveniments als usuaris, però no via l'aplicació web, sinó mitjançant el sistema d'agenda que tinguin (Google calendari, iCal, Yahoo, etc.). Per això és pot fer ús de l'estàndard RFC2445 que permet descriure esdeveniments i altres elements en un format entés per tots els sistemes d'agenda.&lt;br /&gt;&lt;br /&gt;En el meu cas, he emprat l'eina&amp;nbsp;&lt;a href="http://sourceforge.net/projects/ical4j/"&gt;ical4j&lt;/a&gt;. Per no fer massa llarg això, ja ho faré (potser) un altre dia, vaig a mostrar un Servlet d'exemple que publica un calendari amb un sol event:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span style="color: #a10066;"&gt;public&lt;/span&gt; &lt;span style="color: #a10066;"&gt;class&lt;/span&gt; ProvaCalendari &lt;span style="color: #a10066;"&gt;extends&lt;/span&gt; HttpServlet {&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px; min-height: 15.0px;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;span style="color: #a10066;"&gt;protected&lt;/span&gt; &lt;span style="color: #a10066;"&gt;void&lt;/span&gt; doGet(HttpServletRequest req, HttpServletResponse resp)&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;   &lt;/span&gt;&lt;span style="color: #a10066;"&gt;throws&lt;/span&gt; ServletException, IOException {&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px; min-height: 15.0px;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;  &lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Calendar calendar = &lt;span style="color: #a10066;"&gt;new&lt;/span&gt; Calendar();&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; PropertyList properties = calendar.getProperties();&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; properties.add(Version.&lt;span style="color: #2f00cf;"&gt;VERSION_2_0&lt;/span&gt;);&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; properties.add(Method.&lt;span style="color: #2f00cf;"&gt;PUBLISH&lt;/span&gt;);&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; properties.add(&lt;span style="color: #a10066;"&gt;new&lt;/span&gt; ProdId(&lt;span style="color: #5700ff;"&gt;"-//blog/Calendari//CA"&lt;/span&gt;));&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; properties.add(&lt;span style="color: #a10066;"&gt;new&lt;/span&gt; XProperty(&lt;span style="color: #5700ff;"&gt;"X-WR-CALNAME"&lt;/span&gt;, &lt;span style="color: #5700ff;"&gt;"Event S.A"&lt;/span&gt;) );&amp;nbsp;&lt;span class="Apple-style-span" style="color: #319573;"&gt;// Exemple de propietat no estàndard&lt;/span&gt;&lt;/div&gt;&lt;div style="color: #5700ff; font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span style="color: black;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; properties.add(&lt;/span&gt;&lt;span style="color: #a10066;"&gt;new&lt;/span&gt;&lt;span style="color: black;"&gt; XProperty(&lt;/span&gt;"X-WR-CALDESC"&lt;span style="color: black;"&gt;, &lt;/span&gt;"Events per tota la família"&lt;span style="color: black;"&gt;) );&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px; min-height: 15.0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;VEvent event = &lt;span style="color: #a10066;"&gt;new&lt;/span&gt; VEvent();&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;event.getProperties().add(&lt;span style="color: #a10066;"&gt;new&lt;/span&gt; Summary(&lt;span style="color: #5700ff;"&gt;"Cinema a la fresca"&lt;/span&gt;));&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;event.getProperties().add(&lt;span style="color: #a10066;"&gt;new&lt;/span&gt; Uid(&lt;span style="color: #5700ff;"&gt;"id"&lt;/span&gt;));&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;&lt;span style="color: #a10066;"&gt;try&lt;/span&gt; {&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&amp;nbsp;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;event.getProperties().add(&lt;span style="color: #a10066;"&gt;new&lt;/span&gt; DtStart(&lt;span style="color: #a10066;"&gt;new&amp;nbsp;&lt;/span&gt;DateTime(&lt;span style="color: #5700ff;"&gt;"20111128T2200"&lt;/span&gt;,&lt;span style="color: #5700ff;"&gt;"yyyyMMdd'T'HHmm"&lt;/span&gt;,&lt;span style="color: #a10066;"&gt;null&lt;/span&gt;)));&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;} &lt;span style="color: #a10066;"&gt;catch&lt;/span&gt; (ParseException e) {&lt;/div&gt;&lt;div style="color: #319573; font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span style="color: black;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;  &lt;/span&gt;&lt;/span&gt;// Fer el tractament&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;}&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;event.getProperties().add(&lt;span style="color: #a10066;"&gt;new&lt;/span&gt; Location(&lt;span style="color: #5700ff;"&gt;"Parc de la mar"&lt;/span&gt;));&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;event.getProperties().add(&lt;span style="color: #a10066;"&gt;new&lt;/span&gt; Description(&lt;span style="color: #5700ff;"&gt;"The Goonies"&lt;/span&gt;));&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px; min-height: 15.0px;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;  &lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;calendar.getComponents().add(event);&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px; min-height: 15.0px;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;  &lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;resp.setContentType(&lt;span style="color: #5700ff;"&gt;"text/icalendar"&lt;/span&gt;);&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;resp.getWriter().print(calendar);&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px; min-height: 15.0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;}&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;El codi ja ho dic, cerca mostrar un cas més senzill que no pas complet. Un detall important, per exemple, és que s'ha ignorat la qüestió dels "time zones": quan es crea la data és passa null com a paràmetre timezone fent d'aquest un event "flotant". Això vol dir que l'event serà a les 22:00 tant si el client és aquí com a Moscú.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Si fem que aquest Servlet atengui a la petició&amp;nbsp;http://localhost:8080/calendari/ical, ja ens podem subscriure a aquesta adreça com a calendari extern.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Evidentment, al nostre cas, els esdeveniments es consulten d'una base de dades i, per tant, tot el que s'entra mitjançant el backoffice de l'aplicació s'actualitza als calendaris particulars.&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-1050651754641708904?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/1050651754641708904/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=1050651754641708904' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/1050651754641708904'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/1050651754641708904'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2011/11/un-servidor-de-calendari.html' title='Un servidor de calendari'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-2466410797791604344</id><published>2011-11-19T21:53:00.001+01:00</published><updated>2011-11-20T12:26:20.220+01:00</updated><title type='text'>Mòbils i altres productes</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;Vaig comprar fa molt de temps un HTC Magic. El primer mòbil, crec, que va sortir amb Android.&amp;nbsp;Fa bona feina, però ja fa temps que tenc ganes de canviar-ho. Tres anys (més o manco) donen molt per millorar un producte relativament nou com són els telèfons inteligents.&lt;br /&gt;&lt;br /&gt;Així que he estat mirant el que hi ha: els HTC, Googles, els Samsungs, l'iPhone 4S ... I sí, son xulos, però cap m'acaba de convèncer.&lt;br /&gt;&lt;br /&gt;El que no m'agrada és que sembla que estan intentant fer tablets en lloc de mòbils: a veure qui fa la pantalla més gran, posa més MHz o més nuclis als processadords etc. En enginyeria quasi sempre afavorir un factor en penalitza un altre i això és encara més cert quan parlem d'un dispositiu tant limitat com és un telèfon mòbil.&lt;br /&gt;&lt;br /&gt;Tots aquests avanços en pantalla i capacitat de procés acaben castigant, sobretot, la durada de la bateria i, llavors, altres elements com són la comoditat a l'hora d'emprar-ho (per el pes i el tamany) i l'estabilitat del sistema ( inevitable: més complexe, menys estable ).&lt;br /&gt;&lt;br /&gt;Pensant amb alguns dels productes que millor servei me fan, trobo que tenen en comú un disseny molt centrat amb el servei bàsic que han de donar. Per exemple:&lt;br /&gt;&lt;br /&gt;Tenc un rellotge casio que és fell de disseny però te tres característiques fonamentals:&lt;br /&gt;&lt;ol style="text-align: left;"&gt;&lt;li&gt;L'estructura G-Shock ho fa gairebé irrompible&lt;/li&gt;&lt;li&gt;Els vespres sincronitza l'hora amb una senyal de radio d'un rellotge atòmic alemany&lt;/li&gt;&lt;li&gt;És carrega per energia solar&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;Tot això fa que sempre, en qualsevol circumstància, tengui sempre l'hora exacta. No he de comprar piles, ni fer el canvi d'hora, ni llevar-ho per nedar, anar alerta amb els cops, ni res. Sempre, sempre funciona.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Segon exemple: la kindle. No és a color, no permet veure vídeos, no té programes interactius etc. Però fa una cosa: permetre llegir llibres. I això ho fa molt bé.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;El tercer és el portàtil: un mac air, del que ja vaig parlar a&amp;nbsp;&lt;a href="http://domingosebastian.blogspot.com/2011/10/steve-jobs.html"&gt;aquí&lt;/a&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Cap mòbil ara per ara el veig amb les característiques que comentava abans: centrat amb l'ús principal que en faré d'ell. La meva llista de requeriments és aquesta:&lt;br /&gt;&lt;br /&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;Amb un ús normal, la bateria ha de durar 48 hores&lt;/li&gt;&lt;li&gt;Tamanyi pes semblant el HTC màgic&lt;/li&gt;&lt;li&gt;No s'ha de penjar&amp;nbsp;&lt;/li&gt;&lt;li&gt;Una utilitat que té l'android i no hi vull renunciar: tenir els contactes en un sol lloc.&lt;/li&gt;&lt;li&gt;GPS&lt;/li&gt;&lt;li&gt;Aplicacions bàsiques:&lt;/li&gt;&lt;ul&gt;&lt;li&gt;Un bon navegador&lt;/li&gt;&lt;li&gt;Una aplicació d'alarma mínimament intel.ligent que me permeti programar algo tipus: 5:50 de dilluns a divendres. Dissabte i diumenge apagat, per aquests dies ja tenc el menut.&lt;/li&gt;&lt;li&gt;Notificació de mail automàtica&lt;/li&gt;&lt;li&gt;Missatgeria&lt;/li&gt;&lt;li&gt;Calendari&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;div&gt;Renunciaria a:&lt;/div&gt;&lt;div&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;Pantalla gran&lt;/li&gt;&lt;li&gt;Aplicacions sofisticades&lt;/li&gt;&lt;li&gt;Càmara de tropecents megapixels&lt;/li&gt;&lt;li&gt;Video amb HD&lt;/li&gt;&lt;li&gt;Integracions amb facebooks, twitter etc. Per això ja tinc el navegador&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-2466410797791604344?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/2466410797791604344/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=2466410797791604344' title='2 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/2466410797791604344'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/2466410797791604344'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2011/11/mobils-i-altres-productes.html' title='Mòbils i altres productes'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-8513895538872283669</id><published>2011-10-16T19:55:00.001+02:00</published><updated>2011-10-16T19:57:53.135+02:00</updated><title type='text'>Dennis Ritchie</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;Si fa uns dies escribia sobre la mort d'Steve Jobs, ara ho hauria de fer el mateix per Dennis Ritchie.&lt;br /&gt;&lt;br /&gt;Diré la veritat: me fa molta peresa escriure aquesta entrada. Si me va ser fàcil explicar el que me inspirava Steve Jobs, no sé que que dir de Dennis Ritchie. Les seves aportacions són de tant de calat, que no puc ni contestar a la pregunta: com seria la nostra feina sense la seva aportació i la d'en Ken Thompson?&lt;br /&gt;&lt;br /&gt;Només vull anomenar alguns records que me venen al cap dels dies a la universitat. Un llibre de C que me va costar moltíssim (un parell de milers de pesetes) i que vaig llegir i rellegir fins quasi memoritzar-lo. Unes lliçons de com Unix oferia una funcionalitat impresionant a partir d'una sèrie de comandes molt específiques i per si soles senzilles. O com aquest sistema operatiu servia per ilustrar implementacions elegants als problemes més complexes en el disseny de sistemes operatius.&lt;br /&gt;&lt;br /&gt;Ja ho dic, necessitaria molt de temps per escriure aquesta entrada així com cal, però només diré:&lt;br /&gt;&lt;br /&gt;Descansa en pau Dennis Ritchie i moltes gràcies de part dels que ens dediquem a això.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-8513895538872283669?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/8513895538872283669/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=8513895538872283669' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/8513895538872283669'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/8513895538872283669'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2011/10/dennis-ritchie.html' title='Dennis Ritchie'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-2669917826978489809</id><published>2011-10-09T22:10:00.001+02:00</published><updated>2011-10-09T22:10:18.652+02:00</updated><title type='text'>Sandbox</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;br /&gt;La peresa no sempre és dolenta. Ben entesa, millora la qualitat de la nostra feina: simplifiquem el nostre codi, automatitzam coses, evitam duplicats etc. Però normalment la peresa és dolenta.&lt;br /&gt;&lt;br /&gt;Un cas freqüent és quan hem d'incorporar una eina nova o poc coneguda al nostre projecte. Pot ser una llibreria nova per generar fitxers amb un determinat format, una utilitat per fer ordenacions, o altres. Sovint el que feim és incorporar-ho al nostre codi i intentar emprar-ho tal com creiem.&lt;br /&gt;&lt;br /&gt;Segur que tots ho hem fet alguna vegada. És la millor solució? quasi mai. L'aplicació en la que treballam, per poc complexa que sigui, és molt mal lloc per fer proves d'aprenentatge d'una nova eina. Fer això bé (l'aprenentatge) requereix d'un lloc adient. El millor és dedicar un mínim de temps, que sol ser poquíssim, per construir un entorn en que que fer això: una aplicació tipus "hello world" que serveixi per aquest fi. Ja sigui un mètode main, una nova prova unitària, una aplicació web mínima o el que requereixi el que volem provar.&lt;br /&gt;&lt;br /&gt;Quin és el cost de deixar-nos dur per la peresa i no fer-ho?&lt;br /&gt;&lt;br /&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;al final hem perdut temps en lloc de guanyar-lo&lt;/li&gt;&lt;li&gt;la qualitat de l'aprenentatge és pitjor, per el "renou" que suposa treballar amb el sistema real&lt;/li&gt;&lt;li&gt;és difícil que no quedin restes de proves fallides entre el codi definitiu&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;En definitiva: acabem perdent temps, i baixa la qualitat tant del nostre aprenentatge com del codi que finalment queda al projecte.&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-2669917826978489809?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/2669917826978489809/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=2669917826978489809' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/2669917826978489809'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/2669917826978489809'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2011/10/sandbox.html' title='Sandbox'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-2223219047192982820</id><published>2011-10-09T22:07:00.000+02:00</published><updated>2011-10-09T22:07:32.671+02:00</updated><title type='text'>Steve Jobs</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;br /&gt;Vagi per davant que no soc cap "fan boy" d'Apple. Hi ha coses que no m'agraden i que fan que, per exemple, me decanti abans per un mòbil Android que no per un Iphone. Tot i això, també n'hi ha moltes de coses que m'agraden i un dia com avui és amb el que cal centrar-se.&lt;br /&gt;&lt;br /&gt;Tinc un mac air, de 13''. No els de la darrera generació, sinó els de la penúltima. El vaig comprar per desembre de l'any passat i he de dir que ha estat un dels aparells que més satisfet m'ha deixat.&lt;br /&gt;&lt;br /&gt;Qué el fa un producte tan encertat ? La meva opinió és:&lt;br /&gt;&lt;br /&gt;No té entrada de xarxa: cal un adaptador a usb&lt;br /&gt;No té adaptador per projectors: cal també un adaptador s'usb&lt;br /&gt;No té lector de DVD&lt;br /&gt;No té comandament a distància&lt;br /&gt;Només té dues entrades USB&lt;br /&gt;Només és de 13'&lt;br /&gt;Només té 128 GB&lt;br /&gt;&lt;br /&gt;Cada una d'aquestes "característiques" m'ha incordiat en algún moment, però totes elles juntes permeten una experiència única. Tens un ordinador portàtil. Fins aquest moment pensaves que sabies el que era un portàtil, però no. Ara veus els altres i dius: xulo, però no, gràcies.&lt;br /&gt;&lt;br /&gt;No soc expert ni en Apple ni en n'Steve Jobs (ni tan sols estic segur d'estar escriguent bé el seu nom). Però aquesta és la lína que me sembla veure als seus productes: capta l'essencial i fes que això sigui el producte.&lt;br /&gt;&lt;br /&gt;Aquesta simplicitat no és gens fàcil d'aconseguir. La solució que aconseguim, al manco els que no estem tocats per aquesta genialitat, és complexa. Aquesta complexitat reflecteix el nostre coneixement limitat del que feiem. Només moltes iteracions, intentar desfer, refer, tornar a enrera etc., guanyant nova comprensió en cada pas, ens permet aproparnos a una solució més senzilla i neta. La complexitat mostra ignorància, la simplicitat maestria.&lt;br /&gt;&lt;br /&gt;Cercar la simplicitat implica passar de veure una tasca acabada, a veure-la com inacabada. Assumir el risc, l'esforç i el cost de destrossar-la, refer-la i tornar-la a destrossar fins que el resultat ens sigui acceptable. És interioritzar la simplicitat com a valor.&lt;br /&gt;&lt;br /&gt;Crec que el millor homenatge és que tots els que ens dediquem a les noves tecnologies facem més nostre el valor de la simplicitat i fer que la seva pèrdua sigui menor.&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-2223219047192982820?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/2223219047192982820/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=2223219047192982820' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/2223219047192982820'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/2223219047192982820'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2011/10/steve-jobs.html' title='Steve Jobs'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-2165394284063586164</id><published>2011-10-02T21:55:00.000+02:00</published><updated>2011-10-02T21:55:38.161+02:00</updated><title type='text'>N programadors i 1 de sistemes</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;br /&gt;&lt;div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;Aquests darrers dies ha sortit a la feina un tema de discusió: és necessari tenir una persona amb perfil de sistemes al nostre equip?&lt;/div&gt;&lt;div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px; min-height: 14.0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;Per contextualitzar la qüestió: el nostre equip treballa en el desenvolupament i manteniment d'un parell d'aplicacions. Aquestes estan a producció&amp;nbsp;a un centre de dades que duu un altre equip de gent. No ens encarreguem ni dels servidors, ni de la configuració del JBoss, ni de la gestió d'Oracle, ni res d'operacions.&lt;br /&gt;&lt;br /&gt;La nostra tasca diària és, per tant, desenvolupar: obrir l'IDE, programar, probar que funciona i enviar al control de versions. Quin lloc té a aquest equip una persona de sistemes? O, dit de forma més amplia: és convenient tenir una persona a l'equip que no tengui com a tasca programar?&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;Suport a la programació&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px; min-height: 14.0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;Imaginem la vida d'un equip de programació sense una eina de control de versions. Cada programador treballa en una part diferent del programa (difícil fer-ho d'una altre manera) i regularment passa el que fa als altres. Aquests ho incorporen al seu projecte local i escriuen la seva part.&lt;/div&gt;&lt;div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px; min-height: 14.0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;Ningú sap bé mai si te la versió correcte, o més ben dit, sap perfectament que no la té. No és possible accedir de forma sistemàtica a l'estat del projecte un parell de dies després. Tampoc podem saber quines son les diferències entre dues versions etc, etc.&lt;/div&gt;&lt;div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px; min-height: 14.0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;Tots aquests problemes es poden resumir en un: &lt;b&gt;falta de control&lt;/b&gt;. Escribim el nostre codi, però no tenim un control clar del conjunt de codi del projecte. Comparau aquesta situació amb la d'un projecte amb un control de versions correctament implentat.&lt;/div&gt;&lt;div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px; min-height: 14.0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;Podem accedir exactament a tots els estats per els que ha passat el projecte. Podem controlar exactament que tenim al nostre ordinador i com difereix del que hi ha al repositori. Podem tornar enrera. Etc etc etc.&lt;/div&gt;&lt;div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px; min-height: 14.0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;La importància que té el codi fa que sigui impensable treballar sense el soport d'un control de versions, però: és suficient o aconsellable quedar-nos en el codi? o podem extendre aquest requeriment de control a altres aspectes?&lt;/div&gt;&lt;div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px; min-height: 14.0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;Sense limitar la llibertat dels programadors per emprar l'editor que vulguin (som fort partidari d'això), es possible tenir-ne un com a "preferent" a l'equip. Aquest editor ha de ser configurat per adaptar-ho al nostre entorn, això per cada desenvolupador que l'empri. Com ho feim? com el codi sense control de versions, amb cada usuari tinguent la seva versió independent i intentant parlar entre noltros per tenir-ho igual? o centralitzat i amb el mateix control que tenim sobre el codi?&lt;/div&gt;&lt;div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px; min-height: 14.0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;Quin temps duu i quina fiabilitat té la instalació des de zero de l'IDE en l'ordinador d'un nou membre de l'equip? Si algú troba una millora substancial en una opció de configuració, o un nou plugin: quin és el procés per a que tots els atres ho puguin guadir? Com puc tornar a l'estat anterior si, al cap d'un temps, veiem que no era tan bona idea com semblava?&lt;/div&gt;&lt;div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px; min-height: 14.0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;Si programem una aplicació que s'executarà en un servidor d'aplicacions, probablement interessarà que cada desenvolupador tingui el seu propi servidor d'aplicacions en local. Aquest servidor haurà de ser, fins on sigui possible, el més semblant possible al que hi haurà a producció. Quin tipus de control volem tenir damunt la configuració d'aquests servidors?&lt;br /&gt;&lt;br /&gt;Hi ha una tasca important per implantar un sistema de control de configuracions que vagi més enllà del codi font i inclogui totes les eines que afecten a la nostra tasca de programació.&amp;nbsp;&lt;/div&gt;&lt;div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px; min-height: 14.0px;"&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;Coneixements complementaris&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px; min-height: 14.0px;"&gt;&lt;br /&gt;&lt;div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;Crec que hi ha molt poques feines on la formació continua no sigui important. En la major part d'elles, per molt monòtones que semblin, segur que els que hi estan, i les fan amb un respecte cap el seu ofici, poden explicar com ha evolucionat la seva percepció de la feina al llarg del temps. És un aprenentatge continuu. No és, evidentment, una característica exclusiva de la programació, però sens dubte és un dels llocs on més destacada és aquesta necessitat.&lt;/div&gt;&lt;div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px; min-height: 14.0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;El nostre treball és un cicle continuu de aprenentatge i aplicació del que aprenem. Programam, ens topem problemes que no podem resoldre amb el que sabem, invetiguem el tema i incorporam aquests coneixements a la nostra activitat diària. Entre aquestes activitats: programació i (auto)formació, hi ha d'haver un balanç correcte, que cada un de noltres interpretem de forma molt diferent.&amp;nbsp;&lt;/div&gt;&lt;div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px; min-height: 14.0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;N'hi ha que es passen en un sentit: per exemple subordinen la formació a l'activitat i programen sense saber molt bé el que escriuen o quines alternatives hi ha. I n'hi ha que cauen per l'altre costat: és impossible saber-ho tot i no s'atreveixen a escriure res o fan i re-fan continuament.&amp;nbsp;&lt;/div&gt;&lt;div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px; min-height: 14.0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;Per molt partidaris que siguiem de l'autoformació, necessariament ens hem de limitar donat l'abast immens del que podem aprendre i les responsabilitats que tenim. Necessitam establir prioritats i deixar dins les indispensables (el llenguatge de programació que emprem, els bastiments...) i deixar fora les més prescindibles.&amp;nbsp;&lt;/div&gt;&lt;div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px; min-height: 14.0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;Aquesta aritmètica que funciona amb una persona, no té per que aplicar-se igualment a un grup. Si som 10 desenvolupant, ja sí que és rentable i molt que algun d'aquests dediqui temps a aprendre aspectes més de sistemes que poden ajudar a l'equip. Administració bàsica de servidors d'aplicacions, apache, base de dades per tal de poder reproduir el millor possible els entorns de producció o, molt important, per saber interpretar si l'ús que es fa d'aquests recursos és l'òptim.&lt;/div&gt;&lt;div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;br /&gt;&lt;div style="font: 16.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;Llista de responsabilitats&lt;/div&gt;&lt;div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px; min-height: 14.0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;Posats a dedicar una persona a aquest rol, quines serien les seves tasques? Idò entre altres hi hauria:&lt;/div&gt;&lt;div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px; min-height: 14.0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;ul style="list-style-type: disc;"&gt;&lt;li style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;Implementar un sistema de monitorització a l'aplicació per proporcionar a l'entorn d'operacions informació sobre el seu funcionament&lt;/li&gt;&lt;li style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;Interpretar les dades obtingudes amb les eines anteriors a l'entorn de producció o de proves&lt;/li&gt;&lt;li style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;Implementar eines d'integració continua i de control de qualitat&lt;/li&gt;&lt;li style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;Implementar un sistema de gestió de les eines emprades per els desenvolupadors (IDEs, sistemes de build, servidors locals, etc )&lt;/li&gt;&lt;li style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;Ocuparse de problemes d'infrastructura: xarxa, sistema operatiu, seguretat etc.&lt;/li&gt;&lt;li style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;Proporcionar un entorn de proves, automàticament actualitzat amb la darrera versió.&lt;/li&gt;&lt;li style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;Implementar un sistema de proves a distints nivell: funcionals, d'integració, de rendiment etc.&lt;/li&gt;&lt;li style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;Introduir els mecanismes per proporcionar feedback automàtic als desenvolupadors amb les conseqüències dels seus darrers canvis&lt;/li&gt;&lt;li style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;Administrar les eines de suport d'incidències, de control de codi i altres&lt;/li&gt;&lt;li style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;Formar-se en temes no funcionals com escalabilitat, rendiment, seguretat etc... i orientar als desenvolupadors.&lt;/li&gt;&lt;/ul&gt;&lt;div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px; min-height: 14.0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;No sé voltros però jo ho veig molt interessant. Algun voluntari?&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="font: 12.0px Helvetica; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-2165394284063586164?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/2165394284063586164/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=2165394284063586164' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/2165394284063586164'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/2165394284063586164'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2011/10/n-programadors-i-1-de-sistemes.html' title='N programadors i 1 de sistemes'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-55715159494665553</id><published>2011-10-02T20:10:00.000+02:00</published><updated>2011-10-02T20:10:13.414+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='hibernate'/><title type='text'>Hibernate: nombre de resultats i relacions one-to-many</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;Anem a tractar amb un exemple senzill alguns dels aspectes d'Hibernate que poden despistar a l'hora de tractar relacions one-to-many (1 a n).&lt;br /&gt;&lt;br /&gt;Partim d'un exemple amb dues classes entitat: Casa i Persona. En aquest cas, farem la relación one-to-many de Casa cap a Persona: una casa té n habitants, i cada habitant viu a una sola casa.&lt;br /&gt;&lt;br /&gt;Les dades que manejarem són:&lt;br /&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;A la casa amb adreça a la plaça major viuen n'Aina i en Pere&lt;/li&gt;&lt;li&gt;A la casa amb adreça al carrer major, viu només en Joan&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;Comancem amb un llistat de les cases que hi ha amb les dades dels habitants precarregades:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;/div&gt;&lt;div style="font: normal normal normal 11px/normal Monaco; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span style="color: black;"&gt;&amp;nbsp; &amp;nbsp;&lt;query&amp;nbsp;&lt; font=""&gt;&lt;span style="color: #a20092;"&gt;name&lt;/span&gt;&lt;span style="color: black;"&gt;=&lt;/span&gt;&lt;span class="Apple-style-span" style="color: #5700ff;"&gt;"consulta"&lt;/span&gt;&lt;span class="Apple-style-span" style="color: #009695;"&gt;&amp;gt;&lt;/span&gt;&lt;/query&amp;nbsp;&lt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span style="color: black;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; select casa&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span style="color: black;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; from Casa casa&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span style="color: black;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; left join fetch casa.habitants&lt;/span&gt;&lt;/div&gt;&lt;div style="color: #359493; font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&amp;nbsp; &amp;nbsp;&lt;!--&lt;/font--&gt;&lt;span style="color: #009695;"&gt;query&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;span class="Apple-style-span" style="color: #009695;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;Al codi java feim:&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;		&lt;/span&gt;List&lt;casa&gt; resultat = session.getNamedQuery(&lt;span style="color: #5700ff;"&gt;"consulta"&lt;/span&gt;).list();&lt;/casa&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px; min-height: 15.0px;"&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;		&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;		&lt;/span&gt;System.&lt;span style="color: #2f00cf;"&gt;out&lt;/span&gt;.println (&lt;span style="color: #5700ff;"&gt;"se han trobat "&lt;/span&gt; + resultat.size() + &lt;span style="color: #5700ff;"&gt;" elements"&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;		&lt;/span&gt;&lt;span style="color: #a10066;"&gt;for&lt;/span&gt; (Casa casa: resultat) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;			&lt;/span&gt;System.&lt;span style="color: #2f00cf;"&gt;out&lt;/span&gt;.println (casa.getAdreca() + &lt;span style="color: #5700ff;"&gt;" te "&lt;/span&gt; + casa.getHabitants().size() + &lt;span style="color: #5700ff;"&gt;" habitants"&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;		&lt;/span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;En principi podriem esperar que la consulta retornes dos resultats, ja que només hi ha dues cases. Vegem el que diu quan ho executem:&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;se han trobat 3 elements&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;Carrer major te 2 habitants&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;Carrer major te 2 habitants&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;Plaça major te 1 habitants&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;La llista inclou tres elements, ara bé: les dues primeres són per el mateix objecte Java. És a dir:&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;System.&lt;span style="color: #2f00cf;"&gt;out&lt;/span&gt;.println (resultat.get(0) == resultat.get(1));&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;Mostra&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;true&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;Dit d'una altre manera: la llista té tres elements però només dos objectes casa.&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;Per entendre el resultat hem de veure el procés mitjançant el qual Hibernate crea la llista de resultats a partir del que hi ha a la base de dades.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;La consulta anterior genera un SQL com aquest:&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;select casa0_.ID as ID1_0_, habitants1_.PER_ID as PER1_0_1_, casa0_.ADRECA as ADRECA1_0_, habitants1_.PER_NOM as PER2_0_1_, habitants1_.CASA as CASA0_1_, habitants1_.CASA as CASA1_0__, habitants1_.PER_ID as PER1_0__&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;from CASA casa0_ left outer join T_PERSONA habitants1_ on casa0_.ID=habitants1_.CASA&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;Els resultats de l'SQL seran:&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;dades casa Carrer major - dades Aina&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;dades casa Carrer major - dades Pere&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;dades casa Plaça major - dades Joan&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;L'origen de les tres files a la llista de resultats està en que són tres les files que retorna la consulta SQL amb el seu format tabular. Tot i això, les regles estrictes d'identitat per la Session d'Hibernate (a una sessió només hi pot haver un objecte amb el mateix id) fan que les dues primeres entrades acabin representades en el mateix objecte.&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;En aquests casos, ens interesa que la llista de resultats tingui només dos elements (no dupliqui la primera entrada).&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;Hi ha distintes formes de solucionar-ho.&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;En casos senzills, com aquest, ens pot servir afegir un distinct a la consulta. Una altra solució habitual és emprar una estructura de dades que elimini els duplicats ( un Set ).&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;Farem, per exemple:&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;Collection&lt;casa&gt; resultat = &lt;span style="color: #a10066;"&gt;new&lt;/span&gt; HashSet(session.getNamedQuery(&lt;span style="color: #5700ff;"&gt;"consulta"&lt;/span&gt;).list());&lt;/casa&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;O, si volem conservar l'ordre dels resultats de l'HQL:&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;Collection&lt;casa&gt; resultat = &lt;span style="color: #a10066;"&gt;new&lt;/span&gt; LinkedHashSet(session.getNamedQuery(&lt;span style="color: #5700ff;"&gt;"consulta"&lt;/span&gt;).list());&lt;/casa&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;També podem emprar un ResultTransformer (més habitual als Criteria, però també funciona a l'HQL):&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;List&lt;casa&gt; resultat = session.getNamedQuery(&lt;span style="color: #5700ff;"&gt;"consulta"&lt;/span&gt;)&lt;/casa&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;.setResultTransformer(Criteria.&lt;span style="color: #2f00cf;"&gt;DISTINCT_ROOT_ENTITY&lt;/span&gt;).list();&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;Veiem, per tant, que és un tema a tenir en compte però que se soluciona fàcilment. Hi ha, però, una altra qüestió relacionada amb aquest aspecte que és més delicada.&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;És una bona pràctica, per protegir l'aplicació, limitar el nombre de resultats que retorna una consulta. Això ens pot salvar de problemes importants si per error o per males intencions d'algun usuari, una consulta retorna un nombre de resultats tan gran que afecta a la disponibilitat del servidor.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;Per limitar el nombre de resultats feim:&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;List&lt;casa&gt; resultat = session.getNamedQuery(&lt;span style="color: #5700ff;"&gt;"consulta"&lt;/span&gt;)&lt;/casa&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;		&lt;/span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;.setMaxResults(10)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;		&lt;/span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;.list();&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;Si executem això trobarem un warning:&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;WARN org.hibernate.hql.ast.QueryTranslatorImpl - firstResult/maxResults specified with collection fetch; applying in memory!&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;Aquest és un dels warnings als que s'ha de fer cas !&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: black;"&gt;&lt;span style="color: black;"&gt;El problema és que el nombre de resultats és refereix al nombre de cases, mentres que el nombre de files que retorna l'SQL està determinat per el nombre de persones. Per aquest motiu, Hibernate no pot emprar el valor del setMaxResults (10) per limitar via SQL ( numRow ) el nombre de resultats : l'SQL retornarà tots els resultats i només en el procés Java se reduiran a 10 els resultats. Perdem, per tant, el mecanisme de protecció que voliem tenir.&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-55715159494665553?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/55715159494665553/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=55715159494665553' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/55715159494665553'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/55715159494665553'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2011/10/hibernate-nombre-de-resultats-i.html' title='Hibernate: nombre de resultats i relacions one-to-many'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-8215671113112713545</id><published>2011-09-25T12:22:00.000+02:00</published><updated>2011-09-25T12:23:33.502+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='peg revision'/><category scheme='http://www.blogger.com/atom/ns#' term='subversion'/><title type='text'>Subversion: -r (revisió operativa) i @ (peg revision)</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;Treballant en un projecte guardat al subversion és habitual fer còpies, renombrats etc. I això, en general no ens causa gaires problemes. Anem a analitzar un cas a on una combinació de renombrats i creacions de fitxers o directoris pot crear ambiguetats i, per tant, confusions.&lt;br /&gt;&lt;br /&gt;Ens basarem en &lt;a href="http://svnbook.red-bean.com/en/1.6/svn.advanced.pegrevs.html"&gt;l'exemple&lt;/a&gt; que empra el subversion redbook. Partim d'un repositori nou:&lt;br /&gt;&lt;br /&gt;&lt;i&gt;domingo$ svnadmin create repositori&lt;/i&gt;&lt;br /&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;i&gt;domingo$ svn co file:///Users/domingo/blog/repositori/ wc&lt;/i&gt;&lt;br /&gt;&lt;i&gt;Checked out revision 0.&lt;/i&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;(wc és per working copy, no penseu en altres temes ;-) )&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Ara creo un fitxer idea.txt a on plantejo idees preliminars per convertir en un projecte&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;domingo$ echo 'fer un cercador a internet' &amp;gt; idea.txt&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;i&gt;domingo$ svn add idea.txt&amp;nbsp;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;A &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; idea.txt&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;domingo$ svn commit -m "idea per fer un cercador"&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;Adding &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; idea.txt&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;Transmitting file data .&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;Committed revision 1.&lt;/i&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Amb el temps, veig la viabilitat del projecte i creo un projecte real. El contingut d'idea.txt passa a ser el contingut del nou projecte:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;i&gt;domingo$ svn mv idea.txt google.txt&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;A &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; google.txt&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;D &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; idea.txt&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;domingo$ svn commit -m "cercador passa de idea a projecte real"&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;Adding &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; google.txt&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;Deleting &amp;nbsp; &amp;nbsp; &amp;nbsp; idea.txt&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;Committed revision 2.&lt;/i&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;El projecte funciona, tenim centenars de millions d'euros al banc, però això no basta per saciar la nostra fam de projectes nous, així que cream una nova llavor de projecte. Com abans emprem un fitxer idea.txt, ja que ens ha donat sort.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;i&gt;domingo$ echo "fer una web amb missatges dels coneguts" &amp;gt; idea.txt&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;domingo$ svn add idea.txt&amp;nbsp;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;A &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; idea.txt&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;$ svn commit -m "nova idea: una web social"&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;Adding &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; idea.txt&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;Transmitting file data .&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;Committed revision 3.&lt;/i&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;És important destacar que per el subversion, l'idea.txt inicial (revisió 1) i aquesta darrera (revisió 3) no estan relacionats.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I ara ve el problema. Suposau que voleu consultar el contingut del fitxer idea.txt que hi havia a la revisió 1 (les idees per crear un cercador a la web) . Un primer intent podria ser:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;i&gt;domingo$ svn cat file:///Users/domingo/blog/repositori/idea.txt -r 1&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;svn: Unable to find repository location for 'file:///Users/domingo/blog/repositori/idea.txt' in revision 1&lt;/i&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Però, com veiem això falla. Subversion ens diu que no pot recuperar el contingut d'idea.txt en la revisió 1 del repositori. Pensau el que hem dit, aquest darrer idea.txt no està relacionat de cap manera amb l'idea.txt que hi havia a la revisió 1. &amp;nbsp;El fitxer idea.txt que hi ha a la darrera versió del repositori té una història curta: es crea a la revisió 3. No té, per tant, revisió 1.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;El que volem dir-li al subversion és que ens torni el que hi havia a idea.txt a la revisió 1. Per això podem emprar una altra notació anomanada "peg revision". Ho feim amb :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;i&gt;domingo$ svn cat file:///Users/domingo/blog/repositori/idea.txt@1&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;fer un cercador a internet&lt;/i&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;La especificació &lt;url&gt;@&lt;num_revisio&gt; cerca el contingut del repositori a l'ubicació especificada en la revisió &lt;num_revisio&gt;, que és el que voliem fer.&amp;nbsp;&lt;/num_revisio&gt;&lt;/num_revisio&gt;&lt;/url&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Quan empravem -r (s'empra la terminologia revisió operativa), se va a la url especificada a la revisió més recent del repositori, i després es segueix la història cap enrera fins trobar la revisió indicada.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Per exemple: &amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;i&gt;domingo$ svn cat file:///Users/domingo/blog/repositori/google.txt -r 1&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;fer un cercador a internet&lt;/i&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Aquí ens està mostrant el contigut de google.txt a la revisió 1 i ho fa correctament, encara que a la revisió 1, google.txt no existia amb aquest nom, sinó com idea.txt&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-8215671113112713545?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/8215671113112713545/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=8215671113112713545' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/8215671113112713545'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/8215671113112713545'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2011/09/subversion-r-revisio-operativa-i-peg.html' title='Subversion: -r (revisió operativa) i @ (peg revision)'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-5145150669405357250</id><published>2011-09-03T19:53:00.000+02:00</published><updated>2011-09-03T19:53:13.040+02:00</updated><title type='text'>query specified join fetching, but the owner of the fetched association was not present in the select list</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;Si programau amb Hibernate quasi segur que alguna vegada us heu hagut de berellar amb aquest missatge:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;query specified join fetching, but the owner of the fetched association was not present in the select list&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Anem a explicar que vol dir aquest missatge, per que ho dona i entés això, la següent part trivial és com resoldre el problema.&lt;br /&gt;&lt;br /&gt;Treballarem amb un exemple senzill: només tres entitats, Persona, Casa i Municipi. Una persona viu a una casa que pertany a un municipi. La consulta que volem realitzar és quines son les cases de les persones que tenen un determinat nom. Per les cases retornades, volem que tengin inicialitzat les dades referents al seu municipi. Un primer intent ens podria dur a la següent HQL:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;select casa&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; from Persona persona&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; join fetch persona.casa casa&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; join fetch casa.municipi municipi&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; where persona.nom like :nom&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px; min-height: 15.0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Partim de persona, per aplicar el filtre per nom i després lliguem amb casa i amb municipi per tenir les dades ja carregades. L'execució d'aquesta consulta produeix el següent error:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span style="color: #1f0095; text-decoration: underline;"&gt;org.hibernate.QueryException&lt;/span&gt;: query specified join fetching, but the owner of the fetched association was not present in the select list [FromElement{explicit,not a collection join,fetch join,fetch non-lazy properties,classAlias=casa,role=null,tableName=CASA,tableAlias=casa1_,origin=T_PERSONA persona0_,columns={persona0_.CASA ,className=domini.Casa}}] [&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; select casa&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; from domini.Persona persona&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; join fetch persona.casa casa&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; join fetch casa.municipi municipi&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; where persona.nom like :nom&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&amp;nbsp;&amp;nbsp; ]&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;L'errada ens diu que se han proporcionat instruccions de quina informació s'ha de carregar d'un objecte que no forma part de les dades retornades. Anem a veure els dos tipus de joins que hi ha per entendre millor la errada.&lt;br /&gt;&lt;br /&gt;La següent secció explica el funcionament dels join fetch. Si ja el coneixeu botau directament a la secció El problema.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Precarregues&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;El join "normal" (sense fetch) fa un inner join que compleix dues tasques. Quan feim&lt;br /&gt;&lt;br /&gt;from Persona persona&lt;br /&gt;join persona.casa&lt;br /&gt;&lt;br /&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;Afegim una condició adicional: els valors retornats formaran o estaran basats en persones i cases que estan relacionades. Aquest filtre descarta cases o persones sense cap relació.&lt;/li&gt;&lt;li&gt;Defineix l'espai de dades sobre el que definir el que retornem (per exemple l'adreça de la casa, el nom de la persona ....) o els filtres que podem aplicar (per exemple, que les persones tenguin un determinat nom).&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;Si executem:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;select persona&amp;nbsp;&lt;/div&gt;&lt;div&gt;from Persona persona&lt;/div&gt;&lt;div&gt;join persona.casa&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;L'SQL generat és:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;select persona0_.PER_ID as PER1_0_, persona0_.PER_NOM as PER2_0_, persona0_.CASA as CASA0_ from T_PERSONA persona0_&amp;nbsp;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;inner join CASA casa1_ on persona0_.CASA=casa1_.ID&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;Suposem que volem retornar persones (com feim), &amp;nbsp;però a més volem disposar de les dades de les cases de cada persona. La forma de indicar-ho al HQL és ampliar la consulta indicant que s'han de carregar les dades de la casa associada a cada persona. Fer-ho és només afegir la paraula fetch al join:&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;select persona&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;from Persona persona&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;join fetch persona.casa&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;Ara l'SQL generat és:&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;select persona0_.PER_ID as PER1_0_0_, casa1_.ID as ID1_1_, persona0_.PER_NOM as PER2_0_0_, persona0_.CASA as CASA0_0_, casa1_.ADRECA as ADRECA1_1_, casa1_.MUNICIPI as MUNICIPI1_1_&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;from T_PERSONA persona0_&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;inner join CASA casa1_ on persona0_.CASA=casa1_.ID&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;Veim que s'ha ampliat la part del select amb les dades de la casa. Hibernate emprarà aquesta informació per inicialitzar les dades de les persones retornades amb les seves cases.&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;Hibernate empra la informació dels joins i dels where per filtrar les dades que ha de retornar. Un cop té les files que compleixen les condicions es basa en el que hi ha al select i els join fetch per construir i inicialitzar els objectes resultants de la consulta.&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;Anem a fer una representació d'aquestes precarregues. Si feim:&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;select persona&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;from Persona persona&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;join persona.casa&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;el que retorna és persona.&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;Si inicialitzem la informació de casa amb:&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;select persona&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;from Persona persona&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;join fetch persona.casa&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;podriem representar el que retorna com persona -&amp;gt; casa (retorna persona amb casa precarregat)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;amb&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Monaco; font-size: 11px;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;select persona&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;from Persona persona&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;join fetch persona.casa casa&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;join fetch casa.municipi&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;retornaria persona -&amp;gt; casa -&amp;gt; municipi&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;div style="font: normal normal normal 11px/normal Monaco; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: large;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: large;"&gt;El problema&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="font: normal normal normal 11px/normal Monaco; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;/div&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Monaco; font-size: 11px;"&gt;&lt;div&gt;&lt;/div&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;La select problemàtica era:&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;div style="font: normal normal normal 11px/normal Monaco; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;select casa&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="font: normal normal normal 11px/normal Monaco; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; from Persona persona&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="font: normal normal normal 11px/normal Monaco; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; join fetch persona.casa casa&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="font: normal normal normal 11px/normal Monaco; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; join fetch casa.municipi municipi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="font: normal normal normal 11px/normal Monaco; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; where persona.nom like :nom&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="font: normal normal normal 11px/normal Monaco; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; min-height: 15px;"&gt;&lt;/div&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;Podem ignorar el where ja que no afecte a tota aquesta discusió.&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;Segons els fetch, informem a Hibernate que volem tornar: persona -&amp;gt; casa -&amp;gt; municipi, però el select informa que ha de retornar casa !&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;El missatge d'error:&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-family: Monaco; font-size: 11px;"&gt;query specified join fetching, but the owner of the fetched association was not present in the select list&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;indica que el propietari del fetch persona -&amp;gt; casa, això és persona, no està al select, que és casa.&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: large;"&gt;La resolució&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;Entés el problema, la solució és molt fàcil: el tipus de join entre persona i casa és un inner join normal: no s'ha d'afegir cap instrucció de precarrega ja que no retornem persones, sinó cases.&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;Per tant, la select correcte seria:&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;div style="font: normal normal normal 11px/normal Monaco; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;select casa&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="font: normal normal normal 11px/normal Monaco; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; from Persona persona&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="font: normal normal normal 11px/normal Monaco; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; join persona.casa casa&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="font: normal normal normal 11px/normal Monaco; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; join fetch casa.municipi municipi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="font: normal normal normal 11px/normal Monaco; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times; font-size: small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; where persona.nom like :nom&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-5145150669405357250?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/5145150669405357250/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=5145150669405357250' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/5145150669405357250'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/5145150669405357250'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2011/09/query-specified-join-fetching-but-owner.html' title='query specified join fetching, but the owner of the fetched association was not present in the select list'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-7597937371143682002</id><published>2011-08-27T19:45:00.000+02:00</published><updated>2011-08-27T19:45:13.430+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='hibernate'/><title type='text'>Hibernate: Flush, snapshots i rendiment</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;br /&gt;Pensar en Hibernate com un generador d'SQL, impedeix entendre el comportament del framework en moltes circumstàncies.&lt;br /&gt;&lt;br /&gt;Suposo que molts tenim una formació i experiència "esbiaixada" cap a la tecnologia relacional. Per això, quan ens trobem per primera vegada amb una eina ORM com Hibernate, costa desfernos de la forma acostumada de tractar la persistència. Davant un problema, tot d'una veiem la seqüència d'inserts, updates ... que necessitem i mirem d'invocar els mètodes save(), update() ... corresponents.&lt;br /&gt;&lt;br /&gt;Això no només ens duu a un codi innecessàriament complicat i ineficient, sinó que pot dur a "misteris" que interpretem erròneament com bugs d'Hibernate.&lt;br /&gt;&lt;br /&gt;Vegem un exemple:&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="background-color: #cccccc;"&gt;&lt;i&gt;&amp;nbsp; Persona persona = (Persona) session.load(Persona.class, 1L);&lt;/i&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="background-color: #cccccc;"&gt;&lt;i&gt;&amp;nbsp; persona.setNom("Canviat");&lt;/i&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Aquest codi, donat que estigui dins una transacció, farà un sql update. Com és possible si no hi ha cap invocació al mètode update() ?&lt;br /&gt;&lt;br /&gt;Treballant amb Hibernate el nostre focus d'atenció no ha d'estar en la base de dades, com quan treballem amb JDBC. El protagonista en el nostre cas és la sessió d'Hibernate. Un cop un objecte està carregat a la sessió, ja és transaccional. Quan feim el canvi a l'objecte persona, estem indicant que l'objecte persistent canvia el seu estat: llavors ja és responsabilitat d'Hibernate que el canvi arribi a la base de dades.&lt;br /&gt;&lt;br /&gt;Coneixer de quina manera Hibernate realitza aquesta tasca no només és interessant per satisfer la nostra curiositat (que ja seria un motiu suficient). A més, ens permet controlar un aspecte d'Hibernate que té una gran influència en el seu rendiment.&lt;br /&gt;&lt;br /&gt;Les preguntes que hem de contestar són dues: com sap Hibernate quins objectes han canviat i quan envia els canvis a la base de dades.&lt;br /&gt;&lt;br /&gt;Anem a la primera. Quan Hibernate carrega un objecte a la sessió (un load, una consulta, etc), crea una còpia de l'objecte (snapshot). Aquesta còpia no es modifica amb els canvis que posteriorment facem a l'objecte. Aleshores, per saber si un objecte ha canviat o no, basta comparar la còpia amb l'objecte de la sessió: si són iguals no s'ha fet cap modificació, si són diferents sí. En l'exemple que hem plantejat, si el nom de la persona en el moment del load fos "Canviat", no es llançaria l'update encara que haguessim invocat el setNom(…) !&lt;br /&gt;&lt;br /&gt;Segona qüestió: quan s'envien els canvis a la base de dades?. La resposta simplificada seria: el més tard possible. Però clar, això necessita explicar-se una mica.&lt;br /&gt;&lt;br /&gt;Una implementació simplista seria fer un update cada cop que hi ha un canvi. Aquesta estratègia seria molt ineficient. Per exemple:&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="background-color: #cccccc;"&gt;&lt;i&gt;&amp;nbsp; Persona persona = (Persona) session.load(Persona.class, 1L);&lt;/i&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="background-color: #cccccc;"&gt;&lt;i&gt;&amp;nbsp; persona.setNom("Canviat");&lt;/i&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="background-color: #cccccc;"&gt;&lt;i&gt;&amp;nbsp; persona.setTelefon("971555555");&lt;/i&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;El codi anterior, faria dos sql update, un després de cada setXXX. Està clar que el codi anterior només ha de generar un sql update, per tant hem de canviar d'estratègia.&lt;br /&gt;&lt;br /&gt;Retrassar al màxim el moment d'enviar els canvis fa que puguem enviar tots els canvis de persona amb un sol sql update i això és bo. Suposem, per tant, que enviem els canvis just abans de fer el commit. Això ens crea un altre problema.&lt;br /&gt;&lt;br /&gt;Analitzem el següent cas:&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="background-color: #cccccc;"&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp;Persona persona = (Persona) session.load(Persona.class, 1L);&lt;/i&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="background-color: #cccccc;"&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp;persona.setNom("Canviat");&lt;/i&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-tab-span" style="background-color: #cccccc; white-space: pre;"&gt;&lt;i&gt;		&lt;/i&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="background-color: #cccccc;"&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp;long nombre = (Long) session.createCriteria(Persona.class)&lt;/i&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="background-color: #cccccc;"&gt;&lt;i&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;		&lt;/span&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .add(Restrictions.isNotNull("nom"))&lt;/i&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="background-color: #cccccc;"&gt;&lt;i&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;		&lt;/span&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .setProjection(Projections.rowCount())&lt;/i&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="background-color: #cccccc;"&gt;&lt;i&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;		&lt;/span&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .uniqueResult();&lt;/i&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;La consulta retorna el nombre de persones amb el nom distint de null. Si el canvi fet amb el setNom(...) no s'envia abans de la consulta, l'SQL no treballarà amb les dades correctes. En aquest cas, per assegurar un resultat correcte, Hibernate necessita que el canvi fet a la persona sigui enviat a la base de dades abans de fer la consulta! Volem el nombre de persones amb nom no null, i la modificació nostra s'ha de tenir en compte.&lt;br /&gt;&lt;br /&gt;Com una aproximació simplificada (però bastant funcional) podem dir que, per defecte, Hibernate envia tots els canvis a la base de dades abans de cada consulta i just abans de fer el commit.&lt;br /&gt;&lt;br /&gt;Molt bé tot no?, Hibernate s'encarrega de fer els canvis necessaris a la base de dades i a més ho fa de forma transparent i correcte.&lt;br /&gt;&lt;br /&gt;El problema és que aquest funcionament &lt;u&gt;implica un cost en ús de mèmoria i en temps d'execució&lt;/u&gt;. Cada objecte carregat és duplicat en memòria i abans de cada consulta ha de comparar l'estat de tots els objectes que estan a la sessió amb els seus snapshots. Molta feina, eh?&lt;br /&gt;&lt;br /&gt;Podem ajudar a Hibernate indicant-li quan aquestes tasques no són necessàries:&lt;br /&gt;&lt;br /&gt;Amb&amp;nbsp;&lt;span class="Apple-style-span" style="font-family: Monaco; font-size: 11px;"&gt;session.setDefaultReadOnly(&lt;span style="color: #a10066;"&gt;true&lt;/span&gt;);&amp;nbsp;&lt;/span&gt;indiquem que, per defecte, els objectes carregats seran de només lectura i, per tant, no és necessari construir la còpia.&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: left;"&gt;Hibernate anomena flush al procés de revisar quins objectes de la sessió han estat modificats. Si volem deshabilitar els "flushos" iniciats per Hibernate podem fer-ho amb&amp;nbsp;&lt;span class="Apple-style-span" style="font-family: Monaco; font-size: 11px;"&gt;session.setFlushMode(FlushMode.&lt;span style="color: #2f00cf;"&gt;MANUAL&lt;/span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Emprarem el següent bocí de codi per fer un "benchmark" casolà. No el critiqueu, només és per tenir una idea molt aproximada de com pot variar el temps d'execució.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span style="color: #a10066;"&gt;long&lt;/span&gt; inici = System.currentTimeMillis();&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;Session session = getSessionFactory().openSession();&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px; min-height: 15.0px;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;		&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;Transaction txt = session.beginTransaction();&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px; min-height: 15.0px;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;		&lt;/span&gt;&lt;/div&gt;&lt;div style="color: #2f00cf; font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span style="color: #a10066;"&gt;if&lt;/span&gt;&lt;span style="color: black;"&gt; (&lt;/span&gt;optimitzat&lt;span style="color: black;"&gt;){&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&amp;nbsp; &amp;nbsp;session.setDefaultReadOnly(&lt;span style="color: #a10066;"&gt;true&lt;/span&gt;);&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&amp;nbsp; &amp;nbsp;session.setFlushMode(FlushMode.&lt;span style="color: #2f00cf;"&gt;MANUAL&lt;/span&gt;);&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px; min-height: 15.0px;"&gt;}&lt;/div&gt;&lt;div style="color: #319573; font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;// carrega a la sessió de 100.000 objectes&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;List tots = session.createCriteria(Persona.&lt;span style="color: #a10066;"&gt;class&lt;/span&gt;).list();&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px; min-height: 15.0px;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;		&lt;/span&gt;&lt;/div&gt;&lt;div style="color: #319573; font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;// forcem 100 queries&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span style="color: #a10066;"&gt;for&lt;/span&gt; (&lt;span style="color: #a10066;"&gt;int&lt;/span&gt; i = 0; i &amp;lt; 100; i++){&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;&lt;span class="Apple-style-span" style="color: #5700ff;"&gt;&lt;span style="color: black;"&gt;&amp;nbsp; session.createQuery(&lt;/span&gt;"select count(persona.id) from Persona persona where persona.id &amp;lt; "&lt;/span&gt;&lt;span class="Apple-style-span" style="color: #5700ff;"&gt;&lt;span style="color: black;"&gt; + i).list();&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;}&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px; min-height: 15.0px;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;		&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;txt.commit();&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px; min-height: 15.0px;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;		&lt;/span&gt;&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;session.close();&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px; min-height: 15.0px;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;		&lt;/span&gt;&lt;/div&gt;&lt;div style="color: #319573; font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;// mostra el temps en dècimes de segon&lt;/div&gt;&lt;div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"&gt;System.&lt;span style="color: #2f00cf;"&gt;out&lt;/span&gt;.println(&lt;span style="color: #5700ff;"&gt;"durada "&lt;/span&gt; + ((System.currentTimeMillis() - inici) / 100));&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;El temps d'executar això, en funció del valor de la variable optimitzat és:&lt;br /&gt;optimitzat == true : 2.7 segons&lt;br /&gt;optimitzat == false : 12.9 segons&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: left;"&gt;Hi ha diferència eh?&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;Les proves les he fetes emprant una base de dades hsqldb en memòria (al mateix procés). De totes formes, ja dic: més que emprar aquests nombres provau d'afegir aquestes modificacions al vostre codi i observau les diferències.&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-7597937371143682002?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/7597937371143682002/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=7597937371143682002' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/7597937371143682002'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/7597937371143682002'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2011/08/hibernate-flush-snapshots-i-rendiment.html' title='Hibernate: Flush, snapshots i rendiment'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-1372352334683234640</id><published>2011-08-10T19:52:00.000+02:00</published><updated>2011-08-10T19:52:29.803+02:00</updated><title type='text'>Il·lusió de control</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;Fa poc publicava Gallir unes&amp;nbsp;&lt;a href="https://plus.google.com/117939449396284436490/posts/A51fYAbLmu8"&gt;reflexions&lt;/a&gt;&amp;nbsp;sobre la necessitat de tenir plans de contingència en els sistemes informàtics. Justament un parell (mallorquí) de dies després, degut a uns errors en la infrastructura de Amazon, meneame cau i necessita d'unes 48 hores per tornar a estar funcionant.&lt;br /&gt;&lt;br /&gt;No escric per fer sang. Al contrari: els plans de contingència que mostrava Gallir eren (al meu entendre) bons i cobrien el que a priori se podrien considerar com els riscs més probables. En qualsevol cas, el que si crec que convendria rectificar és el to de seguretat excessiva que emprava quan analitzava el tema.&lt;br /&gt;&lt;br /&gt;No vull ser ventagista. No vull parlar només d'aquest cas o d'aquest tema (plans de contingència). És una qüestió de la que fa temps ja feia comptes escriure i això m'ho ha recordat.&lt;br /&gt;&lt;br /&gt;Tant en el treball com en la vida en general, sovint quedem orgullosos o satisfets d'una determinada acció. Això ens produeix una lògica satisfacció, però no ens ha de fer perdre la vista la fragilitat de tot plegat.&lt;br /&gt;&lt;br /&gt;Hi ha un concepte que ilustra això perfectament: l'il·lusió del control.&lt;br /&gt;&lt;br /&gt;Tendim a pensar que el nostre control damunt la situació és molt més alt del que és en realitat. Pot ser sigui convenient o necessari (evita l'estrés continuu de pensar en tots els desastres que poden passar), però l'hem de tenir en compte per compensar els seus efectes: Per exemple, creure que el teu pla de contingència te salva de qualsevol situació.&lt;br /&gt;&lt;br /&gt;Eisenhower va expressar molt bé el que podria ser una recepte per compensar aquest biaix cognitiu. Deia alguna cosa com: els plans no són res, la planificació ho és tot.&lt;br /&gt;&lt;br /&gt;L'important és dedicar temps a elaborar un pla de contingència, no el resultat d'aquest estudi. El pla de contingència és simplement la cristalització del millor que hem pogut pensar fins aquest moment. La diferència és fonamental.&lt;br /&gt;&lt;br /&gt;Posar la importància en l'objecte en lloc del procés pot tenir problemes importants:&lt;br /&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;Quedar en un elegant document, sense, per exemple, provar-ho mai: Fas copies de seguretat? perfecte, però : has provat mai de restaurar el sistema a partir d'aquestes còpies?&lt;/li&gt;&lt;li&gt;No actualitzar-se quan és necessari degut a canvis en l'aplicació o en la infrastructura emprada&lt;/li&gt;&lt;li&gt;Dependre d'accions que no se comproven: segur que el procés que fa còpies de seguretat no ha deixat de funcionar?&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;Una actitut correcte és el que fa que, per exemple, quan incorpores un web service remot a un procés de la teva aplicació et demanis: que puc fer quan això falli?&lt;/div&gt;&lt;br /&gt;Ho trobem en altres àmbits. Experts en seguretat diuen que aquesta no és un producte sinó un procés. No val fer una fase final de desenvolupament dedicada a "afegir seguretat". Per ser efectiva, aquesta s'ha de considerar durant totes les activitats del projecte.&lt;br /&gt;&lt;br /&gt;A Google, qualsevol idea presentada es qüestionada amb un "i això: com escala?".&lt;br /&gt;&lt;br /&gt;Altres exemples més habituals: de poc serveix fer refactoritzacions i millores al codi si no hi ha un compromis en tot l'equip amb el manteniment de la qualitat del codi. &lt;br /&gt;&lt;br /&gt;Intentem interioritzar tot aquest seguit de necessitats no funcionals dels sistemes (disponibilitat, seguretat, escalabilitat, rendiment, etc...) en lloc de centrar-nos en un objecte en concret: una classe ben dissenyanda, un document de contingència o un sistema de control d'accés. Segur que així ens anirà molt millor a tots.&lt;br /&gt;&lt;br /&gt;I, com deia abans, recorda: mai et creguis l'amo de la situació!&lt;br /&gt;&lt;br /&gt;PD: si voleu coneixer la meva opinió del que és un bon pla de contingència aquí la teniu: un passaport fals i 1.000.000 de euros a una compte opaca ;-)&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-1372352334683234640?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/1372352334683234640/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=1372352334683234640' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/1372352334683234640'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/1372352334683234640'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2011/08/illusio-de-control.html' title='Il·lusió de control'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-8136647229386366752</id><published>2011-04-30T18:26:00.000+02:00</published><updated>2011-04-30T18:26:44.003+02:00</updated><title type='text'>Ampliacions</title><content type='html'>&lt;div style="margin-bottom: 0in;"&gt;Quan tractem el tema de modificar l'àmbit d'una aplicació és millor parlar en termes de sistema. Aplicació sembla referir-se només al codi, i deixar de banda molts altres components com són base de dades, servidors, comunicació amb sistemes externs, etc.  &lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;Per tant, la qüestió tractada és, quan ja tenim implentat un sistema de gestió, no acabat, que mai passa, però si ja bastant estabilitzat, com plantejem el seu creixament ?&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;Vaig a expresar quin és el meu punt de vista. No fa falta dir que no soc autoritat en la matèria. No vull ni puc ser dogmàtic. Simplement, escriure quines són les meves idees després de donar-li algunes voltes.&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;Actualment el sistema està cobrint un determinat àmbit. Implementa tot un conjunt de tràmits, informes i permet una sèrie de accions als seus usuaris. D'aquest àmbit l'aplicació implementa un model: algunes coses se consideren i altres, evidentment, les deixa fora.  &lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;Per exemple, l'àmbit inclou el procés de matricula de l'alumne a un centre docent. El model de l'aplicació captura alguns elements, com per exemple el nom de l'alumne, i en deixa alguns altres fora, per exemple, quina és l'altura de l'alumne, per posar alguns casos molt evidents.&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;Quan parlem d'ampliar l'aplicació podem augmenar el detall de l'aplicació (guardem altres dades del procés que ara no tenim) o augmentar l'àmbit (a part del procés de matricula, també implementam l'avaluació que es fa de l'alumne).&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;Les preguntes que hem de respondre són&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;Què ampliam?&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;En quines direccions?&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;b&gt;Què ampliam&lt;/b&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;La meva idea és que s'ha d'ampliar l'àmbit, i només el detall quan sigui molt important per els usuaris.  &lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;De fet, activament s'ha de reduir al màxim el detall que maneja el sistema. És a dir, el model ha de ser el més simplificat possible. Si considerem tot el model com el pes de l'aplicació i l'àmbit com el volum, la idea es pot expressar diguent que l'aplicació ha de ser el menys densa possible.&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;Per prepararnos per l'ampliació de l'àmbit, abans cal un tasca important: desfer-nos de tot el que no és necessari. Dit amb altres termes: aplicar el “abans d'entrar, deixin sortir”.&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;Revisa tota l'aplicació i mira que pots esborrar sense que afecti a les tasques que fan els usuaris. Primer caura, com és lògic,  el codi mort, les taules i les columnes no emprades, etc. Esborra. En cas de dubte, esborra.  &lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;Trobarem també parts de l'aplicació que tenen dades, pantalles de gestió que funcionen, però que no han demostrat utilitat per cap de les accions que fan els usuaris. Aquestes parts, sap greu, però també s'haurien d'esborrar. Passes pena per el codi? està control de versions. Te preocupen les dades? Ja t'ho dic jo: estan malament. Com ho sé? només l'ús freqüent de les dades agaranteix la seva qualitat. Si no s'empren, és molt poc probable que siguin fiables. Si te preocupa, fes una còpia, poseu a un zip i ho guardes. Fet això, esborra les taules i columnes adients.  &lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;Si la teva preocupació és poder justificar la teva tasca davant dels caps, argumentant amb el nombre de taules i línies de codi, no facis el que hem dit abans. Si la teva preocupació és poder respondre a les necessitats dels usuaris de forma eficient, fes-ho. Esborrant el dit abans estalvies temps cada vegada que intentes resoldre una incidència, analitzes on s'empra un recurs del codi, obres l'IDE, fas un checkout, construeixes l'aplicació, fas un commit, treballes amb una branca nova, canvies la versió d'alguna tecnologia emprada, fas una backup, analitzes les dades d'una taula, expliques a un usuari com s'empra l'aplicació, etc, etc, etc.&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;b&gt;En quines direccions&lt;/b&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;Posats a creixer, en quina direcció ho feim?&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;Els objectius. Primer, fer més eficient la tasca dels usuaris, segona, augmentar la seva qualitat.&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;Per exemple: introduir dues vegades les mateixes dades a dues aplicacions diferents penalitza els dos objectius.&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;El primer que cal fer és cercar quin és l'ecosistema d'aplicacions, activitats i fonts d'informació amb les que conviu el nostre sistema. Els distints usuaris no estan tot el dia asseguts emprant únicament la  nostra aplicació. Per complir amb la seva responsabilitat, segur que han de fer tot un conjunt de tasques amb altres aplicacions de la nostra organització, amb aplicacions externes i inclús algunes de forma manual. Per exemple:&lt;/div&gt;&lt;ul&gt;&lt;li&gt;&lt;div style="margin-bottom: 0in;"&gt;Introduixen les mateixes dades a  la nostra aplicació i en una altre altre de la mateixa  organització.&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div style="margin-bottom: 0in;"&gt;Fan unes tasques de forma manual  que tenen algun tipus de solapament amb el nostre sistema. Exemple:  han de escriure a un document de texte documents que la nostra  aplicació podria generar en base a la informació que té.   &lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div style="margin-bottom: 0in;"&gt;Empren altres programes externs,  per exemple, eines organitzatives, que s'han d'emplenar manualment  “transcribint” les dades de la nostra aplicació a aquesta.&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div style="margin-bottom: 0in;"&gt;O, a l'enreves, empren programes  externs que produiexen un resultat que després han de introduir  manualment al nostre programa.&lt;/div&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;Una forma de millorar tota l'organització és oferir als usuaris una aplicació que faci més funcional la colaboració dins de tot aquest ecosistema.&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;Quan considerem que és adient que el nostre sistema ajudi amb la tasca de l'altra producte hi ha dues opcions:&lt;/div&gt;&lt;ol&gt;&lt;li&gt;&lt;div style="margin-bottom: 0in;"&gt;El nostre sistema absorveix la  funcionalitat de l'altre producte&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div style="margin-bottom: 0in;"&gt;Proporcionem una forma de  colaboració entre els dos productes&lt;/div&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;Algunes de les preguntes que ens hem de fer, són:&lt;/div&gt;&lt;ol&gt;&lt;li&gt;&lt;div style="margin-bottom: 0in;"&gt;Quina solució serà més més  còmoda per l'usuari?&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div style="margin-bottom: 0in;"&gt;Quina solució oferirà uns  resultats de millor qualitat?&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div style="margin-bottom: 0in;"&gt;Quina solució ens deixa en millor  situació a l'hora d'agarantir el manteniment de l'aplicació?&lt;/div&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;b&gt;Critèris tècnics&lt;/b&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;Juntament amb els canvis de “esborrar (detall) – afegir (àmbit)” anteriors, s'ha de reservar una quantitat de temps per aspectes més tècnics.&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;Ha passat el temps de les presses, de fer les coses per ahir i de corregir errades greus a producció. És un bon moment per fer les coses amb una mica més de calma, i el primer punt ha de ser reduïr el deute tecnològic adquirit durant el desenvolupament.  &lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;Segur que hi ha parts de l'aplicació que acumulen més errades, que se varen prendre decisions equivocades o que amb el temps hem vist que es podia fer molt millor. És el moment de arreglar aquestes parts. En general, fer net de tot el que està malament o ens fa perdre molt de temps de manteniment.&amp;nbsp;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;Millora la qualitat del codi. Aplica analitzadors i corregeix els defectes que trobin. Mira quines consultes van més lentes i afinales. Revisa els indexos de la base de dades. Redissenya les pantalles més lentes per a que no carreguin tanta informació.&amp;nbsp;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;També s'ha de fer que l'aplicació estigui en linia amb l'evolució de les tecnologies emprades. No es tracta d'emprar sempre la darrera versió, sinó de no despenjar-se. Una transició de la versió 2 a la 3 generalment és senzilla. Passar de la 1 a la 4 pot ser gairebé impossible. S'ha d'anar avançant a la velocitat de la comunitat. Quedar despenjat implica problemes amb la convivència amb  les altres tecnologies, amb l'accés a la documentació i amb l'accés a les correccions dels bugs.&amp;nbsp;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;Finalment s'han de treballar els riscos que pugui tenir el sistema. Pensa en el teu sistema i completa la frasse “quin desastre podria passar si algun dia ...”. Canvia desastre per algun terme més lleu i segur que se t'ocorre alguna cosa. Aprofita ara per eliminar o mitigar aquests riscos tant com sigui possible.&lt;br /&gt;&lt;br /&gt;L'activitat d'esborrat del primer punt t'ajudarà molt en cada una de les tasques anterior.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Per tractar&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;Tot el que he dit és molt simplificat. Just són unes línies per tenir en compte que, en cada cas particular, podran tenir més, menys o cap aplicació. Posats a enumerar algunes de les moltíssimes coses que s'haurien de afegir o matisar:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Quan parlem de simplificar ho feim amb línia amb el "Make everything as simple as possible, but not simpler". És eliminar el no essencial però cercar la riquesa del model a on és necessari.&lt;/li&gt;&lt;li&gt;Mantenir l'acoblament i la cohesió dins del nostre codi. Important quan parlem d'ampliar l'àmbit de l'aplicació.&lt;/li&gt;&lt;li&gt;Una aplicació o distintes aplicacions? Falta tractar el tema de si distribuir l'aplicació i com fer-ho.&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-8136647229386366752?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/8136647229386366752/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=8136647229386366752' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/8136647229386366752'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/8136647229386366752'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2011/04/ampliacions.html' title='Ampliacions'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-698660149165534790</id><published>2011-04-21T12:37:00.000+02:00</published><updated>2011-04-21T12:37:34.678+02:00</updated><title type='text'>Aplicacions de gestió: característiques i implicacions</title><content type='html'>&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;Una característica que m'agrada de les aplicacions és que siguin senzilles. Deia Antoine de Saint-Exupery que la perfecció assoleix no quan no hi ha res més per afegir, sinó quan no hi ha res més per llevar.&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;Hi ha, però, molts tipus d'aplicacions. En la que estic jo és una aplicació de gestió de la Conselleria d'Educació, i aquest tipus d'aplicacions tenen unes característiques bastant especials. Aquestes són els punts que crec les fan diferents a altres tipus d'aplicacions:&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;&lt;b&gt;Completa&lt;/b&gt;&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;Han de ser completes. Altres aplicacions poden centrar-se en fer bé el que representa la majoria de casos. Una aplicació de gestió s'ha d'adaptar a la normativa i cobrir qualsevol cas contemplat, per poc probable que sigui i per molt impedeixi construir una solució més elegant (tots sabem el que molesten les excepcions al cas general).&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;&lt;b&gt;Única&lt;/b&gt;&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;No hi ha competència. No fa falta desenvolupar cap “característica diferenciada” que la imposi a la competència. Evidentment, volem que els usuaris estiguin contents amb l'aplicació, però aquesta satisfacció no necessitem obtenir-la “enlluernant” en el primer instant, sinó més aviat fent que no odiïn l'aplicació després d'emprar-la moltes hores durant molts de dies.&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;&lt;b&gt;Obligada&lt;/b&gt;&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;Els usuaris, en la majoria de casos, l'empren per obligació. Han de realitzar un tràmit o cumplimentar una acció que forma part de la seva tasca. Entren, la fan, i surten.&amp;nbsp;&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;&lt;b&gt;Suport&lt;/b&gt;&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;La tasca principal dels usuaris no és emprar l'aplicació. Els usuaris són professionals amb una altra responsabilitat. L'ús que fan de l'aplicació és per facilitar la gestió de tot el col·lectiu. És un mitjà, no el fi darrer.&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;&lt;b&gt;Controlada&lt;/b&gt;&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;La vida de l'aplicació serà llarga i el seu rumb guiat per factors externs a l'equip de desenvolupament. L'aplicació ha de seguir el dictat d'una normativa que sol canviar de tant en tant, i s'ha d'adaptar a les necessitats i peticions dels seus usuaris.&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;Aquestes punts s'han de tenir en compte a l'hora de desenvolupar l'aplicació. Com afecten? Així ho veig jo:&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;&lt;b&gt;Obligada:&lt;/b&gt;&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;L'usuari no entra a l'aplicació per passar-s'ho bé. El nostre objectiu no ha de ser “retenir-lo” a l'aplicació amb entreteniments, sinó fer que pugui realitzar l'acció que vol fer de forma eficient: sense errades en el procés, amb la major facilitat i en el menor temps. La disponibilitat i el temps de resposta són de gran importància en aquest punt.&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;&lt;b&gt;Suport&lt;/b&gt;&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;L'objectiu de l'usuari no és emprar l'aplicació. Més aviat són usuari i aplicació els que tenen un objectiu comú. L'aplicació ha d'ajudar a l'usuari fent que cada acció realitzada tingui valor per l'objectiu final. Mira en lupa cada dada que demanes a l'usuari: segur que és necessària?&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;&lt;b&gt;Controlada&lt;/b&gt;&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;La normativa que l'aplicació implementa és d'esperar que sigui canviada amb certa freqüència. Aquests canvis poden perjudicar molt a la implementació actual del sistema, però no està en mans dels seus desenvolupadors impedir aquests canvis. Per poder aguantar el viatge és important no dur més equipatge del estrictament necessari. Cada línia de codi s'haurà de mantenir i actualitzar. Cada línia haurà de mostrar que és imprescindible o s'haurà d'eliminar.&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;&lt;b&gt;Completa&lt;/b&gt;&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;La lògica serà complicada. No és tracta de resoldre bé el 80% fàcil dels casos sinó el 100% d'ells. S'ha d'aprofundir en la lògica que hi ha al darrera del seu funcionament per no convertir el codi amb una xarxa de if – elses impossible de mantenir.&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;&lt;b&gt;Única&lt;/b&gt;&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;No hem de convèncer amb 2 minuts a l'usuari de que som la l'aplicació que necessita. No és necessari ni cosmètica ni llumets ni músiques. Introduir funcionalitat orientada a “vendre” l'aplicació, que perjudica a algun dels altres punts, va en contra dels interessos dels usuaris.&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;La metàfora a emprar per aquests tipus d'aplicacions no és la d'un spa on l'usuari cerca comford, relax i passar un parell de dies meravellosos. Seria millor la d'una oficina de tributs, on un usuari que ha demanat una hora lliure a la feina, té el cotxe mal aparcat, i no sap molt bé de que va el tema, ha d'emplenar un tràmit. Millor que no li expliquis que s'ha d'esperar una estona per que has de canviar la música ambiental.&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;Vol dir això que només té cabuda una línia “espartana” de pensament? No. Està clar que encara que siguem en una oficina i no en un spa, un ambient agradable, un tracte amable i certa comoditat són molt ben valorats. No és el mateix posar altres prioritats en primer lloc, que llevar-li tota la importància.&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: arial; font-size: small; margin-bottom: 0in;"&gt;A més, hi ha una lògica econòmica que no es pot ignorar. Així com l'aplicació es va consolidant, la tasca a fer se redueix respecta a la del desenvolupament inicial. Si la dimensió de l'equip es manté, es clar que està en disposició de fer altres tasques a part de just el manteniment del codi actual. Per on es pot ampliar l'aplicació? Aquest serà el proper post.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-698660149165534790?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/698660149165534790/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=698660149165534790' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/698660149165534790'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/698660149165534790'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2011/04/aplicacions-de-gestio-caracteristiques.html' title='Aplicacions de gestió: característiques i implicacions'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-6075469335747206932</id><published>2011-02-23T21:10:00.000+01:00</published><updated>2011-02-23T21:10:55.907+01:00</updated><title type='text'>Madurant</title><content type='html'>Quan estava encara estudiant a la carrera, i durant els primers anys de feina, tenia molt interioritzada la idea del desenvolupament en cascada. De fet, disfrutava molt més fent tasques d'anàlisi que programant. &lt;br /&gt;&lt;br /&gt;L'anàlisi, creia, era la part determinant en la construcció d'un bon producte. Clar que era impossible un bon programa amb un mal codi, però diguem que sí que veia possible aconseguir-ho amb un anàlisi excel·lent i una programació regular.&lt;br /&gt;&lt;br /&gt;Amb aquestes conviccions, el model de desenvolupament òptim reservava molt de temps a la tasca d'anàlisi i, a més, la feia sense "despistar-se" iniciant la programació.&lt;br /&gt;&lt;br /&gt;D'això, ja dic, han passat més de 10 anys.&lt;br /&gt;&lt;br /&gt;No sé en quin moment vaig començar a canviar d'opinió però a dia d'avui ja no coincideix en res amb l'original.&lt;br /&gt;&lt;br /&gt;Suposo que comences a detectar coses que no "quadren" amb les creences inicials. Surten potents entorns 4GL però no arriben a resoldre els problemes. Llevat del casos molt senzills, la complexitat de la lògica supera clarament l'associada a l'estructura de dades. Tot i això, l'anàlisi sembla preocupar-se només (o principalment) per la segona. Models per descriure la part dinàmica són molt inferiors comparats amb els de la part estàtica.&lt;br /&gt;&lt;br /&gt;Comences a observar com la part crítica de l'aplicació, la part que més "inteligència" guarda, queda més al codi que a qualsevol artefacte de l'anàlisi o disseny. Desesperes quan, per molt d'anàlisi que facis, en quan comences a programar i, sobretot, veure als usuaris emprar el sistema, te n'adones del poc que sabies quan feres l'anàlisi.&lt;br /&gt;&lt;br /&gt;Passes d'emprar llenguatges que simplement mouen dades d'un costat a un altre, a altres orientats a objecte que permeten elaborar abstraccions útils, molt semblants a les que empren els experts en el domini. Llavors, en poques (però memorables) ocasions, aconsegueixes que unes poques línies de codi expressin millor que qualsevol diagrama de l'anàlisi, algun aspecte clau de l'aplicació.&lt;br /&gt;&lt;br /&gt;I clar, la idea que tenies quan començares ja no s'aguanta.&lt;br /&gt;&lt;br /&gt;Me fa empegueir la prepotència en que s'afrontava la tasca d'implementació d'un nou sistema. Segons aquella visió (potenciada durant les classes a la universitat), l'analista no havia de deixar-se influenciar excessivament per la forma en que els usuaris feien les coses. L'analista no només implementaria una aplicació, sinó que redissenyeria tot el funcionament. Una postura pedant ... i que crec que te dona molts de números per fracassar. &lt;br /&gt;&lt;br /&gt;Avui, crec que l'actitut correcta és molt més humild. Una metàfora que se m'ocorre és la del que va a viure a l'estranger. Has d'aprendre els llocs, l'idioma, les lleis, però també les convencions socials, les costums etc. del lloc. Has d'oblidar molt del que saps. Has d'evitar al màxim el "però és que al meu país això ho feim així ....". Per molt que llegeixis o t'expliquin, fins que no te instal·les allà no ho coneixes.&lt;br /&gt;&lt;br /&gt;I tot aquest coneixement ha de quedar en el codi i en tots els artefactes vius del sistema: la interfícia, les ajudes, el llenguatge que empres amb els experts i els usuaris, etc. &lt;br /&gt;&lt;br /&gt;Vol dir això que no hi ha anàlisi? No. L'anàlisi és tota la preparació prèvia al trasllat. Aprendre nocions bàsiques de la cultura, una mica de l'idioma... Idees fonamentals per no "morir" durant l'arrivada. Aquesta activitat és molt útil, però la diferència clau està en reconèixer que és limitada. És un esboç inicial que serveix per orientar-nos durant els primers dies però no la referència final del que aprendrem durant l'estància. &lt;br /&gt;&lt;br /&gt;Llevar responsabilitat a l'anàlisi només és negligència si no la translladam a l'activitat de programar. Per això, programar ha d'incloure dues activitats.&lt;br /&gt;&lt;br /&gt;Primera, ampliar i refinar el coneixement i fer que aquest estigui incorporat al codi. Això implica refer parts antigues així com anem coneguent més del sistema. Tenir l'actitud de sospitar de tot el que quedi "estrany" al codi. Interrogar a experts i usuaris de per que fan les coses com les fan i intentar que el codi s'expressi de forma semblant a com ho fan ells.&lt;br /&gt;&lt;br /&gt;Segona, proporcionar un model d'aquest coneixement. Aquesta part és complexe. El programa gestiona molts d'aspectes diferents: persistència i recuperació de dades, gestió de transaccions, de seguretat, construcció de la interfície d'usuari, integració amb altres sistemes, etc. Un model és un esquema simplificat que exclou aspectes importants per deixar només els essencials al que intenta descriure. El nostre codi haurà no només d'implementar tots els aspectes comentat abans, sinó que haurà de proporcionar de forma destacada, un codi que sigui model dels conceptes i les regles bàsiques de negoci.&lt;br /&gt;&lt;br /&gt;No puc estar més d'acord amb el que vaig llegir crec que a Software Craftsmanship: Si tens 10 analistes brillants i 200 programadors regulars, fes fora els 200 programadors i posa a programar als 10 analistes.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-6075469335747206932?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/6075469335747206932/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=6075469335747206932' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/6075469335747206932'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/6075469335747206932'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2011/02/madurant.html' title='Madurant'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-2002699693910198217</id><published>2010-04-23T07:14:00.002+02:00</published><updated>2010-04-23T07:27:43.284+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='hibernate'/><category scheme='http://www.blogger.com/atom/ns#' term='formacio'/><title type='text'>Proper curs d'Hibernate</title><content type='html'>El dia 3 comença el curs d'Hibernate.&lt;br /&gt;&lt;br /&gt;El curs és gratuït i es farà de 16:00 a 19:00, de dilluns a divendres, entre el dia 3 de maig i l'11 de maig.&lt;br /&gt;&lt;br /&gt;Inscripcions a la CAEB (&lt;a href="http://www.caeb.es/component/option,com_wrapper/itemid,151/"&gt;enllaç als cursos&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;Un exemple del material que es donarà:&lt;br /&gt;&lt;br /&gt;&lt;iframe src="http://docs.google.com/present/embed?id=dd4n63xp_159qdkhgjdv&amp;amp;size=m" width="555" frameborder="0" height="451"&gt;&lt;/iframe&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-2002699693910198217?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/2002699693910198217/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=2002699693910198217' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/2002699693910198217'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/2002699693910198217'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2010/04/proper-curs-dhibernate.html' title='Proper curs d&apos;Hibernate'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-4923511117893550274</id><published>2010-03-20T21:40:00.004+01:00</published><updated>2010-03-20T22:03:23.074+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='hibernate'/><title type='text'>Mantenir les files</title><content type='html'>Hi ha situacions en les que els dubtes o la por ens fan prendre decisions incorrectes. La raó es que en aquestes circumstàncies patim una espècie de "visió tunel" que fa que només considerem un aspecte de la qüestió, el més immediat, ignorant-ne d'altres que poden ser molt més importants.&lt;br /&gt;&lt;br /&gt;Un exemple. Un jugador de futbol es necessari que mantengui la seva posició. Encara un extrem tingui la tentació de córrer darrera la pilota per ajudar a tapar la darrera línia, no ho ha de fer. Pot semblar que així ajuda més, però fent això rompe un ordre que és necessari.&lt;br /&gt;&lt;br /&gt;Crec que això passa moltes vegades quan programem amb Hibernate i ens decantem per emprar data transfer objects (DTO), en lloc de les entitats. Veiem més fàcil crear un value object just amb les dades que ens demanen, que no carregar tot el graf d'objectes (possiblement bastant dispers) necessari per obtenir les mateixes dades. Ho resolem amb una HQL o SQL just dels atributs que volem. Ràpid, fàcil i net.&lt;br /&gt;&lt;br /&gt;Però això pot presentar problemes. Problemes, sobretot, quan el programa va evolucionant i comencen a sortir condicions no previstes. Ho veig molt sovint al programa que estem fent. Les entitats van evolucionant conforma canvien els requeriments. És inevitable. Aleshores, els value objects comencen a mostrar problemes.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;El seu disseny, amb el canvi a l'estructura de dades, pot no ser ja tan adient.&lt;/li&gt;&lt;li&gt;Els programadors han d'aprendre múltiples estructures. Coneixen bé les entitats, però a molts llocs és necessari també entendre múltiples estructures dels DTOs.&lt;/li&gt;&lt;li&gt;La lògica queda duplicada en les classes de servei i en múltiples where's&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Conseqüència de l'anterior, perilla la consistència en els resultats obtinguts des de distints tipus d'informes.&lt;/li&gt;&lt;li&gt;Perdem recursos d'Hibernate com filtres o interceptors.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Perdem l'aillament transaccional que ens ofereix la Session&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Se perden les advantatges de les caches de primer i segon nivell.&lt;/li&gt;&lt;/ul&gt;Gairebé sempre me decanto per emprar només entities, però en alguns llocs (sobretot al codi escrit al començament) ho vaig fer amb DTOs. Puc dir que en la immensa majoria de casos, la decisió ha estat incorrecte.&lt;br /&gt;&lt;br /&gt;El consell seria, intenta expresar tota la funcionalitat amb les entitats. Que el codi quedi clar. Només abandona l'estructura de les classes de domini en els llocs on els problemes de rendiment ens obliguin a fer-ho.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-4923511117893550274?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/4923511117893550274/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=4923511117893550274' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/4923511117893550274'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/4923511117893550274'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2010/03/mantenir-les-files.html' title='Mantenir les files'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-5720966858186835453</id><published>2010-03-13T19:01:00.003+01:00</published><updated>2010-03-13T19:07:30.447+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='humor'/><title type='text'>Nombres màgics</title><content type='html'>No poseu mai nombres màgics al codi. Per exemple, si teniu&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;public class Compte {&lt;br /&gt;&lt;br /&gt;   public void processa (Ordre ordre) {&lt;br /&gt;      if (ordre.getId().equals(1038)) {&lt;br /&gt;          // cas especial&lt;br /&gt;      }&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;millor fer&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;public class Compte {&lt;br /&gt;&lt;br /&gt;   public static final int MIL_TRENTA_VUIT = 1038;&lt;br /&gt;&lt;br /&gt;   public void processa (Ordre ordre) {&lt;br /&gt;      if (ordre.getId().equals(MIL_TRENTA_VUIT)) {&lt;br /&gt;          // cas especial&lt;br /&gt;      }&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;i ja està ;-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-5720966858186835453?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/5720966858186835453/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=5720966858186835453' title='1 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/5720966858186835453'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/5720966858186835453'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2010/03/nombres-magics.html' title='Nombres màgics'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-7883835994865763351</id><published>2010-02-22T16:50:00.003+01:00</published><updated>2010-02-22T17:41:46.343+01:00</updated><title type='text'>Introduint Groovy a un projecte Java</title><content type='html'>Aquesta entrada presenta una forma suau de introduir la potència de Groovy al nostre projecte Java.&lt;br /&gt;&lt;br /&gt;Per suau vull dir sense haver de reescriure res del que tenim, simplement conservar-ho tot i anar emprant Groovy als llocs on ens interessa.&lt;br /&gt;&lt;br /&gt;Emprarem un exemple bastant ridícul. No interessa el codi concret, sinó les passes per configurar el projecte.&lt;br /&gt;&lt;br /&gt;Suposem que un Servlet empra una classe d'utilitat Java per construir dinàmicament una plantilla HTML (aniria molt millor un JSP, però ara ho passarem per alt)&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;public void doGet(HttpServletRequest request, HttpServletResponse response)&lt;br /&gt;   throws IOException, ServletException {&lt;br /&gt; response.setContentType("text/html");&lt;br /&gt; PrintWriter out = response.getWriter();&lt;br /&gt; &lt;br /&gt; Persona persona = new Persona();&lt;br /&gt;  &lt;br /&gt; persona.setNom("Tomeu");&lt;br /&gt; ServeiJava prova = new ServeiJava();&lt;br /&gt; &lt;br /&gt; List&lt;string&gt; receptors = new ArrayList&amp;lt;string&gt;();&lt;br /&gt; receptors.add("primer receptor");&lt;br /&gt; receptors.add("segon receptor");&lt;br /&gt; out.println(prova.saluda(persona,"com estas?", receptors));&lt;br /&gt;}&lt;br /&gt;&lt;/string&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;La classe ServeiJava (perdonau el mal exemple!!)  simplement construeix un missatge HTML a partir dels paràmetres&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class ServeiJava {&lt;br /&gt;&lt;br /&gt;public String saluda (Persona persona, String missatge, List&lt;string&gt; destinataris) {&lt;br /&gt;  StringBuilder cadena = new StringBuilder();&lt;br /&gt;&lt;br /&gt;  cadena.append("&amp;lt;html&gt;");&lt;br /&gt;  cadena.append("&amp;lt;body&gt;");&lt;br /&gt;  cadena.append("Això és una cosa que s'ha de comunicar.&amp;lt;br/&gt;");&lt;br /&gt;  cadena.append(missatge).append("&amp;lt;br /&gt;");&lt;br /&gt;  String nom = "";&lt;br /&gt;  if (persona != null &amp;amp;&amp;amp; persona.getNom() != null) {&lt;br /&gt;     nom = persona.getNom();&lt;br /&gt;  }&lt;br /&gt;  cadena.append("Origen: ").append(nom).append("&amp;lt;/br&gt;");&lt;br /&gt;  cadena.append("hi ha ").append(destinataris.size()).append(" destinataris");&lt;br /&gt;  cadena.append("&amp;lt;ol&gt;");&lt;br /&gt;  for (String destinatari: destinataris) {&lt;br /&gt;    cadena.append("&amp;lt;li&gt;").append(destinatari).append("&amp;lt;/li&gt;");&lt;br /&gt;  }&lt;br /&gt;  cadena.append("&amp;lt;/ol&gt;");&lt;br /&gt;  cadena.append("&amp;lt;/body&gt;");&lt;br /&gt;  cadena.append("&amp;lt;/html&gt;");&lt;br /&gt;  &lt;br /&gt;  return cadena.toString();&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/string&gt;&lt;/pre&gt;Podem fer que la classe de servei empri la funcionalitat oferida per Groovy. Per exemple, cream ServeiGroovy.groovy, amb el mètode&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def saluda (Persona persona, missatge, destinataris) {&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;String nom = persona?.nom ? persona.nom: ""&lt;br /&gt;&lt;br /&gt;String llistaDestinataris = ""&lt;br /&gt;destinataris.each {&lt;br /&gt;  llistaDestinataris += "&amp;lt;li&gt; $it &amp;lt;/li&gt;"&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;"""\&lt;br /&gt;&amp;lt;html&gt;&lt;br /&gt;&amp;lt;body&gt;&lt;br /&gt;Això és una cosa que s'ha de comunicar.&amp;lt;br/&gt;&lt;br /&gt;${missatge} &amp;lt;br/&gt;&lt;br /&gt;Origen: ${nom} &amp;lt;br /&gt;&lt;br /&gt;hi ha ${destinataris.size} destinataris&lt;br /&gt;&amp;lt;ol&gt;&lt;br /&gt;  ${llistaDestinataris}&lt;br /&gt; &amp;lt;/ol&gt;&lt;br /&gt;&amp;lt;/body&gt;&lt;br /&gt;&amp;lt;/html&gt;&lt;br /&gt;"""&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;La classe anterior empra un parell de funcionalitats de Groovy&lt;br /&gt;&lt;ol&gt;&lt;li&gt;L'operador save-dereference  (?.) per evitar Null Pointer Exceptions (en Java hem de fer una comparació adicional)&lt;/li&gt;&lt;li&gt;El mètode each amb un closure anònim&lt;/li&gt;&lt;li&gt;El multi-line GString &lt;/li&gt;&lt;li&gt;La forma de retornar valors per defecte&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;Així i tot, el codi Groovy anterior és un poc "javero". No s'ha d'agafar com a exemple de com fer les coses amb Groovy&lt;br /&gt;&lt;br /&gt;Ara falta que l'aplicació web, quan la despleguem, entengui la classe Groovy. Per això farem dues passes més:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;1 Afegir les llibreries de Groovy per el temps d'execució&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Per això hem de incloure al WEB-INF/lib el fitxer groovy-all-X.X.X.jar.&lt;br /&gt;Aquest fitxer pesa uns 5 MB. Una altre opció, si és possible, és deixar-ho dins del lib del servidor d'aplicacions.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;2 Compilar el codi font de Groovy junt amb el de Java&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Per exemple, si emprem ant, seria alguna cosa com això:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;path id="groovy.classpath"&gt;&lt;br /&gt;  &amp;lt;pathelement path="lib/groovy-all-1.7.1.jar" /&gt;&lt;br /&gt;&amp;lt;/path&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;taskdef name="groovyc"&lt;br /&gt;       classname="org.codehaus.groovy.ant.Groovyc"&lt;br /&gt;       classpathref="groovy.classpath"/&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;target name="compile" &gt;&lt;br /&gt;  &amp;lt;groovyc srcdir="src" destdir="${classes}" classpathref="compile.classpath" &gt;&lt;br /&gt;    &amp;lt;javac source="1.5" target="1.5" debug="on" /&gt;&lt;br /&gt;  &amp;lt;/groovyc&gt;&lt;br /&gt;&amp;lt;/target&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Conclusió&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;La incorporació de Groovy al nostre projecte Java no implica ni deixar d'emprar aquest llenguatge ni tirar res del que tenim fet. &lt;br /&gt;&lt;br /&gt;Podem seleccionar un mòdul poc crític o especialment necessitat dels afegits que fa Groovy i provar si aquesta incorporació aporta els beneficis que esperam.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-7883835994865763351?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/7883835994865763351/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=7883835994865763351' title='3 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/7883835994865763351'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/7883835994865763351'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2010/02/introduint-groovy-un-projecte-java.html' title='Introduint Groovy a un projecte Java'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-5159491001203647696</id><published>2010-02-10T22:18:00.000+01:00</published><updated>2010-02-10T22:18:38.623+01:00</updated><title type='text'>Nou curs</title><content type='html'>El març comença la temporada de cursos.&lt;br /&gt;&lt;br /&gt;El primer és un llaaaarg curs que inclou desde la introducció al llenguatge Java fins als Servlets, JSPs i JDBC.&lt;br /&gt;&lt;br /&gt;Més &lt;a href="https://docs.google.com/fileview?id=0B2woKrBZL7DDMDU3NjExMDYtYjc2Ny00ZTM3LWE2ZjgtNGE1NmFiODc2Y2Zm&amp;amp;hl=en"&gt;informació&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;La part de temari no està actualitzada i encara mostra només els continguts referents a la introducció del llenguatge.&lt;br /&gt;&lt;br /&gt;El curs és gratuit i, a més, els que facin bonda seran obsequiats amb una bossa de caramels ;-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-5159491001203647696?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/5159491001203647696/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=5159491001203647696' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/5159491001203647696'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/5159491001203647696'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2010/02/nou-curs.html' title='Nou curs'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-4790043817446342210</id><published>2010-02-09T18:20:00.000+01:00</published><updated>2010-02-09T18:20:13.351+01:00</updated><title type='text'>El nucli</title><content type='html'>En un projecte gran, amb molta gent introduint codi a un ritme alt és difícil mantenir un mínim de qualitat. Les accions que es poden dur a terme, com revisions de codi, refactoritzacions, etc, suposen enderrarir aquest progrés. Al manco a curt termini.&lt;br /&gt;&lt;br /&gt;Quan ens trobem amb aquesta problemàtica: vegem la qualitat com a necessària però som incapaços de arribar al nivell que desitgem, hem de optar per una solució de compromís. Crec, puc estar molt equivocat, que l'opció més adient és identificar quin és el nucli crític de l'aplicació i mantenir-lo el més aïllat possible de la resta de l'aplicació.&lt;br /&gt;&lt;br /&gt;Fins i tot, el millor seria fer que només certes persones fossin responsables d'aquesta part, però això ja pot ser més complicat. Podríem introduir el coll de botella que intentem evitar. Per això una solució més realista probablement sigui mantenir al manco l'aïllament.&lt;br /&gt;&lt;br /&gt;I quina és aquesta part crítica?. En general aquesta part de l'aplicació correspondrà a les classes de domini i mètodes de servei més importat. No de tota l'aplicació, sinó aquelles que representen la part funcional bàsica del sistema. &lt;br /&gt;&lt;br /&gt;No crec que aquesta intenció sigui&amp;nbsp; estranya: ho feim amb altres parts de l'aplicació. Per exemple, si algú ha de crear un Servlet, el crea i ja està. Però si algú necessita una taula nova a la base de dades, normalment feim que necessiti l'aprovació d'algú amb coneixements específics o amb millor visió global de l'aplicació. Moltes vegades inclús es cerca un consens entre un ampli grup de desenvolupadors. No és que el Servlet no sigui important, però està clar que no està al mateix nivell que un canvi a l'esquema de la base de dades.&lt;br /&gt;&lt;br /&gt;Aïllar aquesta zona de l'aplicació que reconeixem com a crítica permet que, encara que no puguem assolir al moment el nivell de qualitat dessitjada, més endavant sigui viable afrontar tasques de millora.&lt;br /&gt;&lt;br /&gt;Per aconseguir aquest aïllament s'han d'evitar dues coses. Que el que ha d'estar allà dins surti, i que el que no ha d'estar allà dins hi entri. Així de fàcil. Així de difícil.&lt;br /&gt;&lt;br /&gt;Per exemple, si feim un mètode de servei&lt;br /&gt;&lt;br /&gt;public void modificaSolicitutVacancies (Solicitut nova);&lt;br /&gt;&lt;br /&gt;Probablement aquesta funcionalitat inclou molts de casos diferents:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;La solicitut canvi l'estat a acceptada. És una acceptació d'una solicitut de vacances&lt;/li&gt;&lt;li&gt;La solicitut canvi l'estat a rebutjada. És una denegació&lt;/li&gt;&lt;li&gt;La solicitut activa un atribut de cancelada. L'usuari retira la solicitut. Si ja estava acceptada s'han de restaurar els dies de vacances restants&lt;/li&gt;&lt;li&gt;...&lt;/li&gt;&lt;/ul&gt;El mètode de servei se converteix amb un simple sql update... Tota la lògica que hi ha al darrera d'aquesta acció, lògica crítica en un sistema de control de vacances, queda en el Servlet, al javascript o, fins i tot, baixa la responsabilitat de l'usuari.&lt;br /&gt;&lt;br /&gt;L'altre problema es pot produir pel fet que, mesclat amb aquesta part crítica, s'hi introdueixin altres components que no ho són tant.&lt;br /&gt;&lt;br /&gt;Per exemple, el sistema anterior pot tenir un servei de notificació via email o sms de l'estat de les solicituts. Aquest clarament és un mòdul de suport a l'aplicació. Pot ser molt important per l'usuari. Fins i tot imprescindible, però no està al nucli del que implementem. Ha d'estar clarament diferenciat al codi.&lt;br /&gt;&lt;br /&gt;El camí a seguir ha de ser tenir el màxim de funcionalitat possible treta de la gestió bàsica de l'aplicació. Quan més ens esforcem en aquesta tasca més parts de l'aplicació passaran a estar implementades amb eines externes en lloc de codi nostre. Coses com eines de reports, eines de cerca de documents o d'explotació de dades (uns companys ara fan virgueries amb el Pentaho), poden augmentar molt el profit que s'extreu de la nostra aplicació.&lt;br /&gt;&lt;br /&gt;Però això només és possible si la base damunt la que se sustenta tota aquesta parafernàlia és sòlida. Si la creació i cerca de documents PDF generats està mesclat amb el nucli de l'aplicació, dificilment trobarem una eina que s'adapti al que necessitem. Si feim un mòdul separat amb el que té de comú tota la part de generació de documents, extreim aquesta part de la gestió bàsica (no mesclar el codi del mètode que determina que una solicitut s'ha d'acceptar amb el que genera el document d'acceptació) i ens adaptem a l'estructura habitual (general) de les eines de indexació i cerca de documents, tenim molts de números de poder delegar part important a una eina externa.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-4790043817446342210?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/4790043817446342210/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=4790043817446342210' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/4790043817446342210'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/4790043817446342210'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2010/02/el-nucli.html' title='El nucli'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-4377734727788248968</id><published>2010-02-07T13:23:00.000+01:00</published><updated>2010-02-07T13:23:36.071+01:00</updated><title type='text'>Creant bits</title><content type='html'>En primer bloc he de donar l'enhorabona a en Ricardo per la seva exposició i a en Toni per la iniciativa. Va ser un capvespre molt ben emprat i entretingut.&lt;br /&gt;&lt;br /&gt;Després de tenir un día per digerir una mica el vist, vaig a escriure el que m'ha quedat del tema.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Impresió general.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Excelent. La capacitat del client a l'hora de configurar el sistema tant de producció com a altres entorns (proves d'acceptació, desenvolupament …)&amp;nbsp; és simplement impresionant. Crec que, un cop generalitzat aquest servei, la tornada enrera es veu molt improbable.&lt;br /&gt;&lt;br /&gt;En Ricardo va fer molt bona feina explicant a un no iniciat que és el que se troba quan se enfronta a l'oferta d'Amazon. Quines parts poden desorientar més i que s'ha de tenir en compte. La part final, amb la demostració, fou tota una exhibició de l'eficiència del sistema. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;El sistema.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;A l'inici de l'exposició en Ricardo va mostrar la seva admiració per la interfícia presentada per el sistema d'Amazon. Deu ser un bon exemple de com s'ha de dissenyar un sistema. Demostració palpable de la importància de construir un model coherent i oferir un conjunt d'interfícies de qualitat.&lt;br /&gt;&lt;br /&gt;En el cas del EC2, se presentaven interfícies RESTFul, SOAP, una utilitat per línia de comandes, una interfícia gràfica i, me va semblar entendre, una API per distints llenguatges. &lt;br /&gt;&lt;br /&gt;Un altre factor que també crec que deu haver influit en l'èxit d'aquest producte és el fet es tracta del sistema que ells mateixos empren. Tot un exemple del principi "eat your own dog food". &lt;br /&gt;&lt;br /&gt;&lt;b&gt;La tasca d'en Ricardo i creant bits.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Me va agradar molt el comentari d'en Ricardo referint-se a que, quan veia la llista final del que necessitava, la veia tan evident que li sobtava haver tingut tants de problemes per arrivar a ella. Aquesta és precisament la tasca dels "pioners" en una tecnologia. Curiosament, quan aquesta tasca està ben feta, el resultat és un "ah, sí, això és molt fàcil", i quan no se fa tant bé, s'obté un "uau, que ets de bo!, això és molt complicat!". Així que enhorabona per la simplicitat a la llista ;-)&lt;br /&gt;&lt;br /&gt;L'arquitectura per Meneame pot servir perfectament com a plantilla per moltes aplicacions Web. De fet, no estaria gens malament que s'oferissen una sèrie de "Architecture patterns" com a punts de partida per configurar els distints projectes.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Alternatives?&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;La que estava en ment de tothom és l'oferida per Google. En Ricardo explica que en les seves proves observaren que la latència en els accessos al BigTable eren molt elevats. Això és un problema important en una aplicació com meneame. No sé si aquest va ser l'argument definitiu, tanmateix Ricardo va valorar el sistema de Google com a molt bo.&lt;br /&gt;&lt;br /&gt;Google i Amazon presenten solucions diferents a problemes semblants. Aquesta diferència és principalment en el nivell d'abstracció que presenten (Disclaimer: no en tenc ni idea del tema. Parlo a partir del que vaig entendre ahir). A Amazon configures el teu sistema i cada una dels seus elements. Has de coneixer molt bé què necessites i quina és la millor forma d'obtenir-ho. Google no et presenta aquest nivell de detall. Tú poses l'aplicació i demanes la capacitat dessitjada. &lt;br /&gt;&lt;br /&gt;Diria que en general (amb excepcions notòries), sol triunfar el sistema que és mou a un nivell d'abstracció més alt. Fent una valoració freda, apostaria fins a 1.5 euros a que les coses li van un poc millor a Google, però no més. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;Programació&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Som programador. D'altres coses (de informàtica i de fora de la informàtica),&amp;nbsp; en sé molt poc.&amp;nbsp; Que en vaig treure de la presentació d'ahir que afecti directament al meu vici?. La primera conclusió va ser que, al manco amb el sistema d'Amazon, el model de programació varia poc. No dic gens. Hi ha coses que s'han de considerar. Per exemple, pot ser necessari modificar el que considerem a l'hora d'optimitzar l'aplicació per incloure factors que afecten al que ens cobra el que ens lloga la plataforma (per exemple, nombre de connexions establertes). &lt;br /&gt;&lt;br /&gt;Un altre problema interessant presentat, l'obtenció de la IP remota quan l'accés arriva per https i ha passat per un balencejador de càrrega que no ha pogut modificar les capçaleres no és inherent al fet d'estar al núvol, sinó a l'arquitectura concreta. &lt;br /&gt;&lt;br /&gt;Ara que tot això és amb el model d'Amazon. Amb Google sí que ens canvia el funcionament d'una pessa clau en tot el sistema: l'abandonament del model de dades relacional, però això ja per un altre dia.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-4377734727788248968?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/4377734727788248968/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=4377734727788248968' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/4377734727788248968'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/4377734727788248968'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2010/02/creant-bits.html' title='Creant bits'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-8506522568649714970</id><published>2009-12-19T10:10:00.000+01:00</published><updated>2009-12-19T10:10:44.194+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='proves'/><category scheme='http://www.blogger.com/atom/ns#' term='llibres'/><title type='text'>llibre "Managing the testing process"</title><content type='html'>Fa unes setmanes vaig comanar un parell de llibres que parlaven de les proves en el software.&lt;br /&gt;&lt;br /&gt;Un d'ells és "Managing the testing process" de Rex Black. Encara no l'he llegit tot (té 600+) pàgines però amb el que duc llegit, més o manco la meitat, ja m'ha servit per fer-me una mica a la idea de com se pot organitzar aquesta tasca.&lt;br /&gt;&lt;br /&gt;En primer lloc he de dir que no era el tipus de llibre que cercava. Volia un llibre a on s'expliquesin tècniques concretes de proves, no la seva gestió. Cercant per amazon durant un parell de dies, me va pareixer que l'oferta de llibres d'aquest àmbit és molt reduida. Al final hem vaig decantar per aquest llibre i "Lessons learned in software testing".&lt;br /&gt;&lt;br /&gt;Cap del dos tracta l'àmbit que cercava, però tot i així m'estan servint per omplir un buid molt important. Al cap i a la fi, ens dediquem professionalment a posar sistemes en producció.&lt;br /&gt;&lt;br /&gt;Intentaré apuntar les idees més importants que m'ha aportat el llibre.&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;La motivació del procés és assegurar al màxim el valor del producte&lt;/li&gt;&lt;li&gt;Per això, s'han d'identificar els riscos (situacions que poden fer perdre aquest valor) i prioritzar-los. Per exemple (simplificat): prob. d'ocurrència x efecte produït&lt;/li&gt;&lt;li&gt;Dissenyar el conjunt de proves (test cases) cercant la màxima cobertura dels riscos anteriors. Molt de pragmatisme és necessari per aquí.&lt;/li&gt;&lt;li&gt;Les proves poden variar molt en la seva especifícitat:&lt;/li&gt;&lt;ol&gt;&lt;li&gt;Proves automàtiques ( de caixa blanca o de caixa negre )&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Proves manuals, però seguint un guió&lt;/li&gt;&lt;li&gt;Proves exploratòries &lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;li&gt;Tots els tipus de proves anteriors tenen valor. En general, anant cap a d'alt són més ràpides d'executar, anant cap abaix, tenen més capacitat de detectar errors.&lt;/li&gt;&lt;li&gt;Les proves automàtiques són molt sexys per els programadors. Com s'ha dit abans tenen valor, però hem d'evitar identificar-les amb TOT el procés de proves.&amp;nbsp;&lt;/li&gt;&lt;li&gt;Una de les principals dificultats que trobarem serà com organitzar l'execució d'aquests tests cases. Les dificultats provenen de que:&lt;br /&gt;&lt;/li&gt;&lt;ol&gt;&lt;li&gt;No treballarem amb una release, sino en successives releases que anirà (anirem) produïnt l'equip de desenvolupament. No podrem passar tots els test cases en cada lliurament.&lt;/li&gt;&lt;li&gt;Entre releases s'introduiran errors de regressió. Que la versió 1.0.RC3 passi el test case X no vol dir que la versió 1.0.RC4 l'hagui de superar.&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/ol&gt;El llibre és molt específic en la gestió del procés. Proposa una metodologia pròpia per el projecte de test i la compara amb l'estàndard IEEE 829. Moltes d'aquestes coses ara mateix no les veig amb possibilitat incorporar-les a la meva feina, però no està de més coneixer les seves idees principals.&lt;br /&gt;&lt;br /&gt;Un consell que m'ha agradat ajuda amb un problema que tots hem patit a les nostres feines: fa referència a la forma de comunicar un bug que hem trobat. Per desgràcia, l'ego dels programadors és molt sensible. Per encara més desgràcia, en alguns casos el manteniment d'aquest ego és més important que la feina que se duu entre mans. Per això, part important de la detecció del bug és justíficar per qué és un bug. L'hem d'aïllar el màxim possible i correlacionar amb una especificació o un dels valors de l'aplicació. És frustrant sovint el que costa convèncer a un programador que un error és un error.&lt;br /&gt;&lt;br /&gt;Per acabar, una idea no del llibre sino del blog de testing de google, per superar la paràlisi a l'hora d'iniciar la definició dels jocs de proves. A la pregunta de "Per on començo a dissenyar les proves", la resposta que donaven era: imagina que d'aquí a dues hores te diuen que posaran el sistema en producció, que seria el primer que provaries? &lt;br /&gt;&lt;ol&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-8506522568649714970?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/8506522568649714970/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=8506522568649714970' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/8506522568649714970'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/8506522568649714970'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2009/12/llibre-managing-testing-process.html' title='llibre &quot;Managing the testing process&quot;'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-3027764155323583699</id><published>2009-12-14T18:48:00.000+01:00</published><updated>2009-12-14T18:48:52.160+01:00</updated><title type='text'>Una conversa estranya</title><content type='html'>Avui he tingut una conversa un tant estranya amb el servei tècnic de vodafone&lt;br /&gt;&lt;br /&gt;Jo: Hola. El móvil no me permite realizar llamadas ni acceder a internet&lt;br /&gt;Ells: (després de 5 minuts d'espera i de demanar-me fins la talla dels calçatins). El problema es que ha superado su límite de consumo, 60 euros&lt;br /&gt;Jo: No sabía que tenía límite de consumo.&lt;br /&gt;Ells: Sí, (40 segons d'espera). Lleva gastados 140 euros&lt;br /&gt;Jo: Ah. Lo del límite no funciona muy bien no?&lt;br /&gt;Ells: ....&lt;br /&gt;Jo: Necesito hacer más llamadas. Pueden anular el límite?&lt;br /&gt;Ells: No. Pero lo podemos pasar a 70 euros&lt;br /&gt;Jo: ??? Así podré llamar?&lt;br /&gt;Ells: Debería funcionar&lt;br /&gt;&lt;br /&gt;Al final li he dit que fes el que trobés per tal que jo pogués telefonar. He rebut un missatge fa poc indicant que s'ha anulat el límit. Ja puc telefonar però encara no tenc accés a internet.&lt;br /&gt;&lt;br /&gt;Espero que el projecte en el que treballo no generi converses com aquesta entre la gent d'atenció als usuaris i les pobres víctimes&lt;br /&gt;&lt;br /&gt;PS: Una conversa sentida a l'avió (també real):&lt;br /&gt;&amp;nbsp;1- Que frio! En XXXX teniamos 22 grados durante la noche&lt;br /&gt;&amp;nbsp;2- ¿En la sombra?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-3027764155323583699?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/3027764155323583699/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=3027764155323583699' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/3027764155323583699'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/3027764155323583699'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2009/12/una-conversa-estranya.html' title='Una conversa estranya'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-1118674202679595134</id><published>2009-10-16T07:36:00.000+02:00</published><updated>2009-10-16T07:36:20.445+02:00</updated><title type='text'>Idioma</title><content type='html'>En tots els projectes en que he participat, hem emprat el català en el codi. Això s'ha fet sense cap discussió, semblava que la opinió era unànime. Però, i si no haguera estat així? i si hi hagués hagut divisió d'opinions?&lt;br /&gt;&lt;br /&gt;Suposo que tots els raonaments haurien estat fets a posteriori. Vull que es faci en català, quins arguments puc emprar? Desgraciadament sembla quasi impossible procedir d'altre forma en aquesta qüestió.&lt;br /&gt;&lt;br /&gt;Pensant-ho fredament, quin idioma s'hauria d'emprar? o, millor: quins arguments hi pot haver per decantar-se per un idioma o un altre?&lt;br /&gt;&lt;br /&gt;Una llengua, si intentem treure factors emocionals que l'enrevolten, és (en l'àmbit que ara ens interessa) una eina de comunicació, i com a tal l'hem d'analitzar en el nostre cas. Quin idioma complirà millor la tasca d'expressar el significat del nostre codi?&lt;br /&gt;&lt;br /&gt;Descomposaria aquesta capacitat en dues propietats diferents: l'univers de persones que podrà llegir el nostre codi i la capacitat expressiva d'aquest idioma en l'àmbit que desenvolupam. &lt;br /&gt;&lt;br /&gt;Anem al primer punt: qui podrà entendre el nostre codi. En aquest cas hi ha factors quantitatius evidents: el nombre de persones que podran treballar en el nostre codi serà molt major si ho fem en castellà, que si ho fem en català. Tots dos, per la seva banda, seran molt inferiors al que tendrem si emprem l'angles. En l'àmbit que treballem, aquestes diferències són pràcticament inexistents (gent de les illes), però se'ns poden ocórrer fàcilment motius per ampliar aquest àmbit. Per exemple, deixar l'aplicació a altres comunitats o fer-la codi lliure. Un altre exemple, accedir a serveis de consultoria externa. Si ho fem en angles podrem treballar més fàcilment amb els experts més qualificats en una determinada tecnologia.&lt;br /&gt;&lt;br /&gt;I que hi ha de la capacitat expressiva que aconseguim amb l'idioma emprat? En principi un podria pensar que és gairebé equivalent en tots ells. O que només és pot veure afectat per el nivell que tenen els programadors (encara que no ho sembli, escric millor en català que en angles;-) ). Hi ha lògica en això. Però hi ha gent que pot objectar coses importants (bàsicament, els seguidors de l'anomenat Domain Driven Design). Aquesta gent diria que és un valor importantíssim desenvolupar un llenguatge comú entre desenvolupadors i clients que estigui present en tots els àmbits: comunicació directe, documentació, codi, taules, etc. Desenvolupar aquest llenguatge no es tant triar quina paraula emprarem per expressar cada concepte sinó esforçarnos per enraonar amb els mateixos conceptes. No dir habitació si els professionals del sector empren el terme "unitat d'allotjament". Però més que això, entendre per que el terme habitació no és exacte i s'empra l'altre. &lt;br /&gt;&lt;br /&gt;Aquesta tasca serà molt més fàcil si emprem el mateix idioma en el que s'expresen els usuaris, la documentació oficial i les altres aplicacions que conviuran amb la nostra. En el nostre cas, aplicacions per la Conselleria d'Educació, el català és la llengua més adient. Si l'aplicació hagués de gestionar la docència a totes les comunitats, això canviaria. &lt;br /&gt;&lt;br /&gt;Afegiria, però, que la solidesa d'aquest segon factor està condicionat a la importància que donem al model del domini. Si a l'hora de fer l'anàlisi, el criteri emprat és "basta per implementar els requeriments" poca importància té que hagem de traduir els termes emprats del català al castellà o l'angles.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-1118674202679595134?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/1118674202679595134/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=1118674202679595134' title='2 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/1118674202679595134'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/1118674202679595134'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2009/10/idioma.html' title='Idioma'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-470861648002527480</id><published>2009-09-25T15:30:00.000+02:00</published><updated>2009-09-25T15:30:24.889+02:00</updated><title type='text'>tancat?</title><content type='html'>Sense portatil, crec que aquest bloc estara un temps aturat: -(&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-470861648002527480?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/470861648002527480/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=470861648002527480' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/470861648002527480'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/470861648002527480'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2009/09/tancat.html' title='tancat?'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-8358696468007032460</id><published>2009-09-12T19:18:00.000+02:00</published><updated>2009-09-12T19:18:06.010+02:00</updated><title type='text'>Robatori</title><content type='html'>Un mal dia.&lt;br /&gt;&lt;br /&gt;M'han robat un parell de coses del cotxe quan estavem nedant a Alcanada. Una bossa on l'atlota tenia el passaport, la camara de fotos, targetes de banc i altres, i una altra bossa on duia dos portàtils, el que empro per treballar a casa i un antic on juga la nina.&lt;br /&gt;&lt;br /&gt;Havia agafat el portàtil per acabar de preparar el curs de desenvolupament amb JBoss que comença dilluns.&lt;br /&gt;&lt;br /&gt;Quasi tot ho tenia al subversion de la feina, però no havia pujat els projectes amb els exercicis, ni una darrera presentació.&lt;br /&gt;&lt;br /&gt;Ara estic preparant a contrarellotge tot el que falta.&amp;nbsp; Espero poder arrivar amb alguna cosa presentable.&lt;br /&gt;&lt;br /&gt;Que hi farem ...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-8358696468007032460?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/8358696468007032460/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=8358696468007032460' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/8358696468007032460'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/8358696468007032460'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2009/09/robatori.html' title='Robatori'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-3180820978630690000</id><published>2009-09-09T21:03:00.000+02:00</published><updated>2009-09-09T21:11:44.752+02:00</updated><title type='text'>Esborrats</title><content type='html'>Excelent article sobre l'&lt;a href="http://www.udidahan.com/2009/09/01/dont-delete-just-dont/"&gt;esborrat d'entitats&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Vist via en &lt;a href="http://in.relation.to/Bloggers/GreatPostAboutDatabaseSoftDeletes"&gt;Gavin King&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Crec que falta comentar que quan ens plantejem un esborrat hi pot haver dues situacions al darrera:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Una acció sobre l'entitat que identifiquem com a eliminació. El cas tractat a l'article.&lt;/li&gt;&lt;li&gt;Desfer una creació incorrecte. És a dir, esborrem un producte per que ens hem equivocat a l'entrada. Aquest producte no ha existit mai. En aquest sentit, sí és correcte l'eliminació.&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-3180820978630690000?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/3180820978630690000/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=3180820978630690000' title='2 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/3180820978630690000'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/3180820978630690000'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2009/09/esborrats.html' title='Esborrats'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-7232348818656092717</id><published>2009-08-22T22:00:00.000+02:00</published><updated>2009-08-22T22:16:23.487+02:00</updated><title type='text'>Què vols ser de major?</title><content type='html'>Recordo vagament que hem feien la pregunta quan era petit. No sé que contestava, futbolista, policia, bomber o alguna cosa que surtis per la tele, suposo. També jo demano això a la meva filla major. Ella me contesta metjesa. Ja veurem, la pregunta és més un joc que no una predicció.&lt;br /&gt;&lt;br /&gt;Jo al final m'he fet programador, ara tinc 35 anys i a la feina torna a sortir la pregunta: i tú, que vols ser de major?&lt;br /&gt;&lt;br /&gt;Buf, programador, suposo. Però per la reacció de l'interlocutor sembla que la resposta és igual de lògica que si a n'en Messi li demanen que vol fer als 50 anys i contesta seguir de davanter al Barça. Així que torna-m'hi. Que vull ser de major?&lt;br /&gt;&lt;br /&gt;Com que sembla que t'expulsen de la feina de triada, has de mirar quines opcions te queden. En l'àmbit en el que estic hi ha dos camins típics: informàtic funcionari o professor a instituts. Cap de les dues opcions m'estira massa. Posats a triar, aniria a oposicions però sembla que a partir d'ara hi haurà poques places.&lt;br /&gt;&lt;br /&gt;Sense entrar en el terreny de "mel i sucre" de treballar per l'administració, l'altre possibilitat que sol apareixer és la de convertir-te en cap. O sigui, entrar més en tasques de gestió. Vaig fer una llista de les deu qualitats principals d'un cap i vaig fallar en onze (una era posar nombres a les llistes per no descontar-me).&lt;br /&gt;&lt;br /&gt;Fent l'exercici de retirar les opcions que m'agraden però sembla que no puc fer (programar), les que no m'agraden (funcionari, professor) o les que no valc (jefe, gigolo, futbolista, etc.), que triaria del que queda?&lt;br /&gt;&lt;br /&gt;Per ara se m'ocorren dues coses que m'agradaria fer:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;treballar en un equip de test. És un tema que me interessa de fa temps i hi vaig fent lectures en "background". A més, me fa l'efecte que els problemes de la meva "vellesa" que m'impediran seguir programant no tindran tant d'efecte en les tasques de fer proves.&lt;/li&gt;&lt;li&gt;donar classes a la universitat. Ja sé que he dit que no m'agrada la docència, però són els adolescents els que no m'agraden. Me fan por ;-). Crec que amb alumnes universitaris sí que m'agradaria. Pot ser una any d'aquests intenti entrar com a professor extern (ara no sé quin nom li donaven a això).&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;El que sí que temp clar que és que d'aquí a 15 anys no se programarà com avui així que el més important és llegir moltíssim i entendre el que està vinguent de nou. Al manco intentarem posar-lis difícil el retirar-mos.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-7232348818656092717?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/7232348818656092717/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=7232348818656092717' title='11 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/7232348818656092717'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/7232348818656092717'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2009/08/que-vols-ser-de-major.html' title='Què vols ser de major?'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-4261663650095249840</id><published>2009-07-19T17:36:00.000+02:00</published><updated>2009-07-19T17:56:21.714+02:00</updated><title type='text'>Problemes, causes i solucions</title><content type='html'>Que van de bé a vegades les crítiques.&lt;br /&gt;&lt;br /&gt;A un mateix no, que això molesta i som molt sensibles. Però sí als altres. En concret, al Java, que és al que em dedico.&lt;br /&gt;&lt;br /&gt;La proliferació de nous entorns i llenguatges ha fet evidents alguns defectes molt importants del desenvolupament tradicional en Java. No té sentit negarlos, ja que són evidents. El que sí es pot debatre és si aquests defectes són inherents al propi llenguatge, i per tant, no hi ha solució possible, o bé si són per la forma actual de plantejar algunes coses.&lt;br /&gt;&lt;br /&gt;Tot això ve a que trobo que el Java (entorn, no llenguatge) està responent bé als reptes plantejats.&lt;br /&gt;&lt;br /&gt;Anem als problemes més comentats.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Cicle codificació - prova&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Un dels defectes importants són els temps de desplegament per poder provar canvis en el nostre codi. L'altre dia &lt;a href="http://domingosebastian.blogspot.com/2009/07/millorant-la-productivitat-en.html"&gt;parlava&lt;/a&gt; d'una forma de fer que el contenidor tingués accés als nostres canvis en fitxers JSPs o estàtics sense haver de redesplegar el context Web. Clar que això és una part petita.&lt;br /&gt;&lt;br /&gt;Un pot argumentar, amb raó, que els canvis més freqüents són en el codi Java. Per això es pot fer servir una eina que, per ara, m'ha convençut plenament: el &lt;a href="http://www.zeroturnaround.com/javarebel/"&gt;JRebel&lt;/a&gt;. En la nostra aplicació (EJBs, Servlets, més de 2500 classes ...) puc provar un canvi en una classe Java directament al JBoss en uns 2 o 3 segons!. Segur que altres entorns ho poden fer una mica més ràpid, però no molt més. Me falta poder augmentar la velocitat de prova d'un canvi a una classe de domini (o que afecti a Hibernate) però ja arribarà.&lt;br /&gt;&lt;br /&gt;Tot això afegit a que el fet de que moltes coses es poden (i s'haurien) de provar amb proves unitàries, d'execució molt ràpida, fa que al llarg del dia les aturades de servidor o redesplegaments es puguin reduir a un nombre molt baix.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Complexitat&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;La complexitat de les tecnologies d'infrastructura estan en bon camí de la ma d'Spring. En cada versió es fa més fàcil resoldre coses com l'ús de les connexions, accessos a serveis remots etc.&lt;br /&gt;&lt;br /&gt;Buf, molt curt i gens argumentat, però m'interessa anar als següents ;-)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Productivitat&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Pel que fa a la productivitat, per aquí hi ha dos camins. Seguir el camí "dinàmic" amb "convention over configuration" o Groovy, Grails, Griffons i companyia o mantenir-se en la línia ortodoxa: tipat i fora "màgia". En aquest segon cas hi ha una eina que ens ajuda a tenir una versió funcionant en pocs minuts: &lt;a href="http://www.springsource.org/roo"&gt;Spring Roo&lt;/a&gt;. Aquesta eina ens genera tot el codi "pesat" i de "copy-paste" de cada projecte amb un parell de sentències.&lt;br /&gt;&lt;br /&gt;La raó de emprar Spring Roo és atacar el problema de la productivitat amb èxit però disposar de tota la infrastructura Spring tradicional per poder afrontar aplicacions més complexes.&lt;br /&gt;&lt;br /&gt;Aquest és un problema complexe. Per un costat tenim molt de codi idèntic. L'hem generat ràpid, però encara el tenim. Sabem dels problemes de les duplicitats. Necessitem moltíssim més codi per fer una interacció bàsica i típica que l'equivalent amb Grails. Ara bé, tot el que Grails no escriu es basa en convencions que fan que el nostre codi sigui una mica "màgic". Màgic en sentit negatiu: l'aplicació fa coses que no són explícites al codi. Clar que no s'ha de confondre màgic amb aleatòries, tot està ben definit, només que no en el nostre codi, sinó en l'entorn.&lt;br /&gt;&lt;br /&gt;També hem de tractar aquesta qüestió quan emprant Spring, hem de decidir fins quin punt emprem les annotacions per retirar l'XML de configuració. Es pot arrivar a deixar un XML quasi buid però no sempre ens convé renunciar a la definició centralitzada que suposa l'application-context.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Llenguatge&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Les crítiques més doloroses, en la meva opinió, són les que fan referència al propi llenguatge. En Java necessitem moltes volteres per implementar coses senzilles com "retorna la nota mitjana dels alumnes d'aquesta llista, considerant només els aprovats". Aquí sovint se identifica aquesta limitació per la naturalesa "tipada" del Java. De fet, es sol contrastar la "xapussaria" que ha de fer el Java amb solucions fetes en llenguatges dinàmics, que ho resolen de forma (molt) senzilla i (molt) elegant. "Lo cortés no quita lo valiente" ;-)&lt;br /&gt;&lt;br /&gt;Estan sortint algunes llibreries que faciliten resoldre algun petit subconjunt d'aquests problemes. En particular, m'ha agradat molt el &lt;a href="http://code.google.com/p/google-collections/"&gt;google-collections&lt;/a&gt;, però ja dic, no tracta ni de bon troç l'arrel del problema. Donat que el llenguatge ja té els seus anys i s'està mostrant conservador, cal cercar ajuda fora d'ell.&lt;br /&gt;&lt;br /&gt;I fora d'ell trobam tot un conjunt de llenguatges com &lt;a href="http://groovy.codehaus.org/"&gt;Groovy&lt;/a&gt; i &lt;a href="http://www.scala-lang.org"&gt;Scala&lt;/a&gt; que aporten aire fresc a on abans només hi havia humitats. L'avantatge d'aquests llenguatges és que s'executen a la mateixa màquina virtual, oferint interoperabilitat amb el codi Java. És a dir, podem seguir desenvolupament en Java però fer un mòdul en Groovy si aquest llenguatge és més adient per les responsabilitats del mòdul.&lt;br /&gt;&lt;br /&gt;Vull dir amb tot això que el Java aguantarà l'envestida que reb?. No. Ni idea del que passarà. Pot ser el Java hagui d'acabar dient alguna cosa com "Als que m'heu de matar: moltes gràcies"&lt;br /&gt;&lt;br /&gt;Només dic que degut al que han proposat els llenguatges i entorns alternatius, avui desenvolupar en Java és molt millor que fa un parell d'anys.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-4261663650095249840?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/4261663650095249840/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=4261663650095249840' title='2 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/4261663650095249840'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/4261663650095249840'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2009/07/problemes-causes-i-solucions.html' title='Problemes, causes i solucions'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-927899488449005311</id><published>2009-07-05T18:45:00.000+02:00</published><updated>2009-07-05T18:54:21.325+02:00</updated><title type='text'>Millorant la productivitat en aplicacions web J2EE</title><content type='html'>&lt;div&gt;Una de les protestes més habituals que hi ha quan es desenvolupa en J2EE, és el temps que s'està per provar una cosa.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;El cicle escriure, compilar, crear war (i/o ear), desplegar, provar i tornar a començar pot ser desesperant.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;L'inconvenient és més notable si el que volem fer és canviar un simple html, o javascript o fins i tot un JSP.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Vaig a proporcionar un petit "truc" que vos pot ajudar si patiu el que hem dit abans.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Per començar heu de tenir, si no ho teniu, una estructura de directoris idèntica a la que queda al war. És a dir, un directori WEB-INF amb el web.xml, un directori classes i altres trastos per el contenidor, i a l'arrel els JSPs i continguts estàtics. A aquest directori poseu-li el nom que tendria el war, per exemple aplicacio.war.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Tot fet?, seguim.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;La idea és indicar al servidor que aquest directori, l'aplicacio.war (sí, és estrany noms de directori amb extensió, però no hi faceu cas), ha de ser desplegat.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Per començar arrenquem el servidor JBoss de forma normal. Fet això ens situem amb la consola al directori bin del JBoss. Allà hi ha un script per enviar comandes al JBoss: el twiddle.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Bàsicament al twiddle podem enviar via la línia de comandes el que es pot fer via l'aplicació jmx-console. El que direm serà: desplega l'aplicació que està al directori X (on X és el nostre aplicacio.war). Au idò:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;C:\java\jboss\jboss-3.2.7-caib1\bin&gt;twiddle -s localhost invoke "jboss.system:service=MainDeployer" deploy "file:/C:/la_vostra_ruta/aplicacio.war/"&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I això és tot. Als logs del JBoss hauria de sortir una cosa semblant a:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;18:22:15,109 INFO  [TomcatDeployer] deploy, ctxPath=/aplicacio, warUrl=file:/C:/la_vostra_ruta/aplicacio.war/&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Anau amb el navegador a alguna pàgina de la vostra aplicació. Va bé?&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Si vos ha fallat comprovau que heu posat la barra final. Si no la poseu JBoss no arrancarà el desplegament d'un directori sinó el de un fitxer war, que no és el cas.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Si vos falla amb el jboss-3.2.8-caib5, mala sort, aquesta versió no està soportada en aquest blog. Sincerament, vos aconsello que desenvolupeu en un altre JBoss i tengueu una màquina amb la versió jboss-3.2.8-caib5 per provar de tant en quant que tot funciona. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Seguim? Bé, suposo que tot vos ha anat bé. Ara ve l'efecte "uau". Amb el vostre editor modificau un fitxer jsp (o html, css etc.). Guardeu els canvis i refrescau el navegador. Xulo eh? Res de redesplegar aplicacions per veure els canvis en els fitxers modificats. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Si és a on feis feina habitualment, això ha de multiplicar la vostra productivitat. Fàcilment es passa d'un temps de 15 segons o un minut (en funció del tamany de l'aplicació) a un de 2 o 3 segons.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;En cas de canviar el web.xml, o recompilar un servlet executem la comanda anterior però ara canviant el deploy per un redeploy:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;C:\java\jboss\jboss-3.2.7-caib1\bin&gt;twiddle -s localhost invoke "jboss.system:service=MainDeployer" redeploy "file:/C:/la_vostra_ruta/aplicacio.war/"&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Encara en aquest cas hi hauria d'haver una millora important en el temps de prova degut a que no s'ha de empaquetar, moure i desempaquetar tot el contingut de la part web.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Aquesta estratègia es pot emprar encara que tingueu EJBs (eecs) o fins hi tot Hibernate (aaaah). Simplement els altres components els desplegau en artefactes separats (ears, jars o wars, segons el cas) i feis que la vostra aplicació web accedeixi a ells via JNDI.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Espero que vos sigui útil!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-927899488449005311?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/927899488449005311/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=927899488449005311' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/927899488449005311'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/927899488449005311'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2009/07/millorant-la-productivitat-en.html' title='Millorant la productivitat en aplicacions web J2EE'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-7748528377153330137</id><published>2009-04-19T19:48:00.000+02:00</published><updated>2009-04-19T20:07:40.773+02:00</updated><title type='text'>Google collections</title><content type='html'>Una de les coses que més m'agrada del que he vist de Groovy són els closures (no és propi de Groovy, però és on ho he vist). És el lloc on més evident se m'ha fet la quantitat de codi que arrives a escriure per expresar una funcionalitat evident.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Com que per ara no podem emprar Groovy, he pegat una ullada al &lt;a href="http://code.google.com/p/google-collections/"&gt;google-collections&lt;/a&gt;. Evidentment no fa tot el que es poden fer amb els closures, però pot substituir-lo en algun cas d'ús.&lt;/div&gt;&lt;div&gt;Aquí hi ha algunes proves:&lt;/div&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;final Alumne DANI = new Alumne(2L, "Dani");&lt;br /&gt;final Alumne PEP = new Alumne(1L, "Pep");&lt;br /&gt;&lt;br /&gt;Multimap &amp;lt;Alumne, Matricula&gt; matricules = ArrayListMultimap.create();&lt;br /&gt;&lt;br /&gt;System.out.println("carrega a partir de tuples alumne - matricula");&lt;br /&gt;&lt;br /&gt;// això, amb els collection, es un rollo. Ara queda molt bé:&lt;br /&gt;matricules.put(PEP, new Matricula(1L, "mates", PEP));&lt;br /&gt;matricules.put(PEP, new Matricula(2L, "fisica", PEP));&lt;br /&gt;matricules.put(DANI, new Matricula(3L, "fisica", DANI));&lt;br /&gt;&lt;br /&gt;System.out.println("----------- totes les matricules per alumnes -----------");&lt;br /&gt;for(Alumne alumne : matricules.keySet()) {&lt;br /&gt;   System.out.println("matricules de : " + alumne.getNom());&lt;br /&gt;   for (Matricula matricula: matricules.get(alumne)) {&lt;br /&gt;       System.out.println("- " + matricula.getEstudis());&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;System.out.println("----------- acumulat -----------");&lt;br /&gt;System.out.println("nombre de matricules d'en pep : " + matricules.keys().count(PEP));&lt;br /&gt;&lt;br /&gt;System.out.println("----------- matricules de fisica -----------");&lt;br /&gt;final Iterable&amp;lt;Matricula&gt; matriculesDeFisica = Iterables.filter(matricules.values(), new Predicate&amp;lt;Matricula&gt;() {&lt;br /&gt;   public boolean apply(Matricula input) {&lt;br /&gt;      return input.getEstudis().equals("fisica");&lt;br /&gt;   }&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;for (Matricula matricula : matriculesDeFisica) {&lt;br /&gt;   System.out.println("matricula = " + matricula.getAlumne());&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;final Ordering&amp;lt;Map.Entry&amp;lt;Alumne,Collection&amp;lt;Matricula&gt;&gt;&gt; comparadorPerNombreDeMatricules =&lt;br /&gt;Ordering.natural().onResultOf(new Function&amp;lt;Map.Entry&amp;lt;Alumne,Collection&amp;lt;Matricula&gt;&gt;, Comparable&gt;() {&lt;br /&gt;    public Comparable apply(Map.Entry&amp;lt;Alumne,Collection&amp;lt;Matricula&gt;&gt; matriculesDeAlumne) {&lt;br /&gt; return matriculesDeAlumne.getValue().size();&lt;br /&gt;    }&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;final Map.Entry&amp;lt;Alumne, Collection&amp;lt;Matricula&gt;&gt; alumneAmbMesMatricules =&lt;br /&gt;Collections.max(matricules.asMap().entrySet(), comparadorPerNombreDeMatricules);&lt;br /&gt;System.out.print("alumne amb mes matricules : " + alumneAmbMesMatricules.getKey().getNom());;&lt;br /&gt;System.out.println(" te : " + alumneAmbMesMatricules.getValue().size() + " matricules");&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;System.out.println("----------- Creem estructura estudi -&gt; alumnes ----------- ");&lt;br /&gt;Multimap&amp;lt;String, Alumne&gt; alumnesPerEstudi = ArrayListMultimap.create();&lt;br /&gt;for (Map.Entry&amp;lt;Alumne, Matricula&gt; tupla : matricules.entries()) {&lt;br /&gt;   alumnesPerEstudi.put(tupla.getValue().getEstudis(), tupla.getKey());&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;System.out.println("tots els alumnes ");&lt;br /&gt;for(String estudis : alumnesPerEstudi.keySet()) {&lt;br /&gt;   System.out.println("matricules de : " + estudis);&lt;br /&gt;   for (Alumne alumne: alumnesPerEstudi.get(estudis)) {&lt;br /&gt;      System.out.println("- " + alumne);&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;Me falta mirar-ho més. Algunes de les coses anteriors me fa l'efecte que s'han de poder fer de forma més senzilla.&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-7748528377153330137?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/7748528377153330137/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=7748528377153330137' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/7748528377153330137'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/7748528377153330137'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2009/04/google-collections.html' title='Google collections'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-8897677120490873977</id><published>2009-04-04T14:40:00.000+02:00</published><updated>2009-04-04T15:07:19.888+02:00</updated><title type='text'>Publicitat de cursos gratuïts</title><content type='html'>Volia incorporar publicitat a l'entrada anterior i m'he oblidat. Quin comercial que som :-(&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Ja s'han iniciat els cursos de Java i familia que faig a la CAEB. Són cursos &lt;span class="Apple-style-span" style="font-weight: bold;"&gt;gratuits &lt;/span&gt;per gent que treballi, estigui d'autònom o a l'atur. No hi poden participar els treballadors de l'administració.&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Els cursos es fan de dilluns a divendres de 16:00 a 19:00 a les oficines de la CAEB. Hi ha cursos d'Ajax, Spring, Hibernate, JBoss i altres.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Aquí teniu el &lt;a href="http://www.domingosebastian.com-a.googlepages.com/catalogo.pdf"&gt;catàleg&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Si algú es vol apuntar pot trobar la informació a la &lt;a href="http://www.caeb.es/component/option,com_wrapper/itemid,151/"&gt;CAEB&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Es important que la gent interessada s'apunti en les properes dues setmanes (encara que el curs es faci després de l'estiu ! ). El motiu és que d'aquí poc es recolliran les fitxes dels interessats, és farà la selecció, i fins i tot, es cancelaran els cursos que no tinguin un mínim d'apuntats.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Ens veiem els cursos, o no ;-)&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-8897677120490873977?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/8897677120490873977/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=8897677120490873977' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/8897677120490873977'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/8897677120490873977'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2009/04/publicitat-de-cursos-gratuits.html' title='Publicitat de cursos gratuïts'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-6826132882200777467</id><published>2009-04-04T14:22:00.000+02:00</published><updated>2009-04-04T14:40:18.880+02:00</updated><title type='text'>Discusions</title><content type='html'>&lt;div&gt;... fa molt de temps ..&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Cada matí vaig amb el bus que passa per Selva a les 6:30. Feia poc temps de la instalació del sistema de targeta per pagar els viatges amb tren i bus, i aquest era el tema de moda en les conversacions amb els conductors. El fet que aquests sapiguessen que jo soc programador feia el tema encara més obligatori.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;El conductor protestava del fet que la targeta reconeixia correctament el fet que agafar primer el bus i després el tren formava part d'un mateix viatge (consumia només un dels viatges que hi havia a la targeta), però no a l'enreves. És a dir, si el trajecte era Palma - Selva, el tren et restava un viatge (correcte) però llavors, al pujar el bus, la màquina et consumia un segon viatge (error). &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;El conductor me interrogava sobre el per que es torbaven tant en corregir això. El diàleg va ser una cosa així:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Conductor - no entenc per que ho fa bé amb un sentit i amb l'altre no&lt;/div&gt;&lt;div&gt;jo - s'hauria de mirar com està fet. Pot ser les màquines del tren i les del bus son diferents o hi pot haver altres coses que no sabem&lt;/div&gt;&lt;div&gt;c - això no és així (gran expresió). Si ho fa bé en un sentit i en l'altre no és fàcil d'arreglar. Així com deixes passar la corrent en un sentit, la deixes passar en un altre.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Aquí vaig necessitar uns instants per assimilar l'afirmació anterior.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;jo (ja recuperat) - No. Això no va així (a mí no me va quedar tan bé). El programa no veu l'electricitat, treballa d'una altra forma.&lt;/div&gt;&lt;div&gt;Conductor - Ja ho sé que no veu la corrent. Es molt poca. Però si atures la corrent l'ordinador deixa de funcionar, no?&lt;/div&gt;&lt;div&gt;jo - Sí (vaig haver d'admetre)&lt;/div&gt;&lt;div&gt;Conductor - idò (sentencià)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Vaig passar la resta del viatge pensant que si no havia guanyat aquesta discusió ja no en guanyaria cap a la meva vida.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;.... a dia d'avui&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Amb aquests antecedents comprendreu quin és el meu ànim quan he de discutir un tema tècnic amb un company. Si al meu capet en una frase apareixen les paraules discutir i tècnic, un nom sortirà de forma automàtica. Hola ja saps qui ;-)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;El company afirmà que es troba més còmode quan un cas d'ús es troba implementat en un sol mètode. Definir capes, com ara la de servei i la de repositori o dao, només fan més difícil localitzar les errades. La reutilització dels mètodes més granulars, per exemple els de la capa dao, és una mite. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;També vaig necessitar un temps per recuperarme de l'afirmació però no tant ja que no m'agafà massa per sorpresa. Aquesta qüestió surt molt sovint. Recordo fa temps parlar amb una persona d'un nivell tècnic molt alt, que desjectà l'ús d'Spring. Trobava que era molt pràctic que el fluix del codi, des del moment en que s'inicia el cas d'ús fins que es resol fos evident. Capes i dependències només ho complicaven. També aquesta setmana, parlant amb una altra persona d'un nivell tècnic excelent, questionava la necessitat de tenir una capa de DAOs amb EJBs 3.0. Aquest company meu diria que això s'ha convertit amb el meu "caballo de batalla".&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;A mí (i a molts) el que m'agrada és que cada classe i cada mètode tengui una única responsabilitat, molt clara. Forçosament això fa que un cas d'ús necessiti passar per distintes classes. Un conjunt de mètodes especialment importants son els que estan en la capa de servei. Aquests són els punts d'entrada del cas d'ús i mostren a alt nivell (delegant les operacions de més baix nivell) com se implementa el cas.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Sol succeir que, al començament, aquesta lògica és trivial. Posem que un professor abandona el centre. En aquest cas el que s'ha de fer és posar una data de baixa a un registre de la taula de destinacions. En pseudocodi:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;baixaDestinacio (codiDestinacio):&lt;/div&gt;&lt;div&gt;   ajudaBD.baixaDestinacio(codiDestinacio)&lt;/div&gt;&lt;div&gt;   &lt;/div&gt;&lt;div&gt;en aquest cas, sembla innecessari tenir una capa que només fa propagar les cridades a la capa de base de dades. Podriem fer:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;baixaDestinacio(codiDestinacio):&lt;/div&gt;&lt;div&gt;   // codi bbdd per fer update a la taula de destinacions&lt;/div&gt;&lt;div&gt;  &lt;/div&gt;&lt;div&gt;I tot més senzill, no?&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;El problema és quan se comença a compliar el cas. I SEMPRE passa.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Si el professor tenia permisos especials aquests s'han d'esborrar.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;baixaDestinacio (codiDestinacio):&lt;/div&gt;&lt;div&gt;   professor, centre = obteDadesDestinacio(codiDestinacio)&lt;/div&gt;&lt;div&gt;   ajudaBD.baixaDestinacio(codiDestinacio)&lt;/div&gt;&lt;div&gt;   ajudaDB.baixaPermisos(professor, centre)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;uep... el professor pot tenir dues destinacions al centre. Només hem de donar de baixa els permisos si aquesta era la darrera destinacio.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;baixaDestinacio (codiDestinacio):&lt;/div&gt;&lt;div&gt;   professor, centre = obteDadesDestinacio(codiDestinacio)&lt;/div&gt;&lt;div&gt;   ajudaBD.baixaDestinacio(codiDestinacio)&lt;/div&gt;&lt;div&gt;   si ( no ajudaBD.teDestinacions(professor, centre ) &lt;/div&gt;&lt;div&gt;      ajudaDB.baixaPermisos(professor, centre)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I si hi ha diferències en cas que la destinació fos per substituir-ne una altre? o si ...&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Hi ha moltes dificultats en la construcció d'un projecte. Optimitzar consultes, emprar bé les sessions, aspectes de seguretat, però una de les que més me preocupa és que les regles de negoci estiguin clarament implementades. Per clarament vull dir que les pugui veure quasi igual que com les veuria escrites en texte. Que les pugui mostrar a una persona no tècnica i amb una mica d'ajuda me pugui dir si està ben implementat. No he necessitat 80 linies de texte per explicar el que s'ha de fer quan un professor deixa una destinació i no les vull al meu mètode. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Si haguessim evolucionat aquest cas amb l'aproximació monolítica el mètode baixaDestinació tindria més de 50 línies i, encara que el codi fos fàcil, no ho seria veure amb claretat la lògica expresada en el pseudocodi anterior.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;L'accés a base de dades sempre ocupa "espai" i una certa especialització. Per a que aquest espai no "enmascari" la lògica del cas d'ús és necessari implementar-la en una altre classe i exposar-la amb un nom clar.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Quan tenc temps, m'agrada "passetjar" per codi ben escrit. Un dels que hem desperta més admiració és el d'Spring. És tot un exemple de com organitzar el codi d'un sistema gran i complexe. Si intentem seguir un cas senzill, com per exemple fitxar la propietat a un bean, podem tornar beneits entre wrappers, delegates, helpers ... El cas pot ser tan senzill com fitxar un valor boolean true a una propietat d'un bean. Per que tant de trui?. Si seguim l'execució arrivem a una classe CustomBooleanEditor que converteix un String a Boolean a un mètode setAsText, gran tasca no? Bé, pot ser sigui una tasca petita però al manco està aillada amb una classe especialitzada. La classe accepta els valors yes, no, true, false, on, off, 0 i 1. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Si algun dia volem canviar això (acceptar pozi i pono) sabem on anar i és fàcil fer-ho. Només ho haurem de canviar a un lloc. Si hi ha un error reconeguent valors boolean, podem comprovar fàcilment si el problema és aquí o no. Podem escriure una classe de test que validi la lògica. Tot el codi de "pegamento" que ajunta i articula les parts involucrades en fitxar una propietat a un valor (que són moltes), no solen ser gaire relevants un cop fetes. Al arrivar el moment de mantenir i evolucionar l'aplicació són els mètodes com setAsText que han d'anar canviant. L'objectiu és treballem amb mètodes clars i centrats i no amb mètodes de 400 linies que fan el que ens interessa i 10 altres coses més.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-6826132882200777467?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/6826132882200777467/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=6826132882200777467' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/6826132882200777467'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/6826132882200777467'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2009/04/discusions.html' title='Discusions'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-4600342488958563573</id><published>2009-03-15T10:35:00.000+01:00</published><updated>2009-03-15T10:45:41.260+01:00</updated><title type='text'>Lliçons de la vida real</title><content type='html'>&lt;span class="Apple-style-span" style="border-collapse: collapse; color: rgb(0, 0, 0); font-family: arial; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: pre-wrap; widows: 2; word-spacing: 0px;"&gt;Tinc la mala costum de no desconnectar gaire de la feina. Els viatges d'anada i tornada amb tren a la feina són sovint les parts més productives de la meva vida laboral. Tinc el portàtil a la sala i sempre que estic allà, encara que estigui fent altres coses, el miro de reull pensant amb el que me falta per fer. Abans de dormir no puc evitar fer balanç del fet i del que he de fer. Mala cosa.&lt;br /&gt;&lt;br /&gt;Fa uns dies estava pensant amb la necessitat de canviar de cotxe, cosa que no podré fer fins que la hipoteca no estigui més controlada. Llavors vaig pensar amb el natural que és veure dos cotxes diferents, semblants per els ulls no experts, on un val el doble o el triple que l'altre. La qualitat, penses. I és això, clar. Tots dos et poden dur d'un lloc a l'altre, però aquí acaben les similituds. Seguretat, fiabilitat, comford, consum i tota una sèrie de paràmetres força objectius serveixen per veure les diferències entre totes dues opcions i prendre una decisió correcte. En programació costa explicar que una mateixa aplicació es pot fer amb un any o mitj o amb tres o amb més de quatre segons la qualitat que es vulgui.&lt;br /&gt;&lt;br /&gt;En un altre moment vaig acompanyar la meva filla al bany per a que fes el que es fa allà. Quan va acabar li vaig recordar que havia d'estirar la cadena. Com la majoria de banys moderns, no hi ha cap cadena que estirar, sinó que s'ha de premer un botó per a que tot quedi net. "Bon exemple per els cursos", vaig pensar. Un cas de violació important del principi de l'abstracció és emprar noms per indicar com es fa una cosa, en lloc de que fa. Si no passa el que passa.&lt;br /&gt;&lt;br /&gt;Cas final. Estava assegut sopant amb la família mentre la filla, que ja estava molt cansada, discutia amb la seva mare, que estava encara més cansada. Jo estava pensant amb unes coses que havia llegit de Groovy i no hi feia cas (que ho puc ser, de mala companyia !), però la meva sogra que havia llegit una revista de fer punt i no li havia creat tants dubtes, estava més pendent de la discussió. Aleshores va intentar intervenir però no va fer més que empitjorar la situació. La mare es va enfadar amb ella per interrompre. Va dir alguna cosa tipus "Fitxat amb en Domingo, que sap que ens ha de deixar fer i que ho he de resoldre jo". Evidentment jo vaig fer veure que la meva falta d'actuació era precisament per aquest motiu, i no que tingués el cap en una altre banda. On és la lliçó? Bé, està agafat una mica per els pels.&lt;br /&gt;&lt;br /&gt;Diria que quan programam hem de confiar en una sèrie de tecnologies de les que depenem. Confiar i respectar. Està molt relacionat amb el post anterior. Necessitem entendre les motivacions i la forma d'afrontar la seva responsabilitat, de cada tecnologia emprada. HTTP, HTML, CSS, Java, Spring, Hibernate, JTA, el model relacional etc. Si no ho feim, sovint veurem com a defectes coses que no ho són, i els intents de ficar-hi ma per arreglar-ho no faran més que empitjorar la situació.&lt;br /&gt;&lt;br /&gt;Aprofito per fer publicitat d'un curs gratuit d'eines de desenvolupament en Java que comença el dia 23 i te bastantes places lliures.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.caeb.es/cursos/FichaCurso.php?id=751748"&gt;http://www.caeb.es/cursos/FichaCurso.php?id=751748&lt;/a&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-4600342488958563573?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/4600342488958563573/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=4600342488958563573' title='1 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/4600342488958563573'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/4600342488958563573'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2009/03/llicons-de-la-vida-real.html' title='Lliçons de la vida real'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-8692006922232149531</id><published>2009-03-07T09:56:00.000+01:00</published><updated>2009-03-07T10:03:10.828+01:00</updated><title type='text'>Hibernate 3</title><content type='html'>&lt;div&gt;Aquesta ha estat una de les setmanes més complicades a la feina. La tasca a fer era actualitzar la versió d'hibernate emprada, de la versió 2.1.6 a una versió 3. &lt;/div&gt;&lt;br /&gt;&lt;div&gt;Hi havia motius de pes per fer-ho. Dos dels que més ens afectaven era un bug a les consultes escalars que feien que les columnes sortissen desordenades. Com que el resultat el recuperaves com a un Object[], en la majoria de casos no tenia solució. L'alternativa era retornar un Data Object, però no sempre era el que te interessava.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;L'altre cosa que havia molestat era que les Criteria no es podien ordenar per camps que no fossin de l'entity root. Com que quasi sempre l'ordenació es feia per una relació amb les traduccions, això descartava o dificultava l'ús de les criteria, en llocs on, per altra banda, podien fer molta bona feina.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Aquests dos motius, encara que importants, no eren determinants. Una tercera limitació molt més important i que feia molt de temps que hem llevava la son estava relacionada amb la limitació del nombre màxim de resultats d'una consulta. L'API d'Hibernate, tant la versió 2 com la 3, permet fitxar el nombre màxim de resultats en una query HQL o en una Criteria. L'SQL resultant (quan generem consultes sobre Oracle) és semblant a això:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;select * from ( select camps from taula where restriccions order by ordre ) where rownum &amp;lt; limit &lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Aquesta forma, encara que sembli estranya a primera vista (no és evident que sigui cap optimització) es reconeguda per Oracle com un cas especial de consulta i fa un tractament molt eficient.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;El problema està que quan Hibernate 2 generava la consulta interna (la de dins del parèntesis), posava camps duplicats a la select. Això feia que la consulta externa fos sintàcticament invàlida. És a dir, no podíem emprar, en la majoria de casos la limitació de resultats. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I això sí que és greu. A l'aplicació hi ha consultes sobre taules molt poblades. En alguns casos, si no es controla poden implicar carregar desenes de milers d'objectes en la memòria del servidor d'aplicacions, i això no pot ser mai bo per la salut del JBoss. D'ençà que hem començat el projecte vaig mantinguent mentalment una llista top-3 dels motius que poden comprometre l'estabilitat del sistema, i aquest problema, des de feia unes setmanes, anava líder.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Així que dit i fet. El canvi el veia com a imprescindible i aquesta darrera setmana s'ha fet. I, quina setmana!. Tot i que ja feia alguns dies que anava actualitzant les consultes i mapeijos per a que funcionassin amb Hibernate 3, encara s'ha hagut de fer una feinada. I suposo que encara hi haurà llocs a on aniran sortint coses.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Per que ha estat tant complicat canviar la versió d'Hibernate? Sens dubte, per que hi havia moltes coses que estaven mal fetes. Moltes coses seguien una lògica SQL impecable ... però no estaven emprant SQL sinó HQL. Amb Hibernate 2 funcionaven ... per casualitat. Amb la 3, no.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Evidentment no és fàcil canviar la forma de pensar i resoldre problemes quan es passa d'un llenguatge tan eficient i conegut com és l'SQL a un diferent, que empra una forma molt distinta de representar la informació, com és Hibernate. Hibernate fa coses millor que treballar directament damunt el model relacional, i fa coses pitjors. Tot i això, si fas una aplicació amb Hibernate, t'has d'esforçar a "adaptarte" a les seves regles.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Suposem que estàs una mica cansat de la vida d'Occident, amb el seu estres, individualisme etc. i decideixes anar-te'n a viure a la India, a un poblat més o manco retirat de les grans ciutats. Segur que trobaràs això que cerques ... però també hi haurà coses del lloc a on has viscut tota la teva vida que trobaràs a faltar. En aquest moment pots pensar alguna cosa tipus: ja ho sé, intentaré tenir el millor de les dues bandes, i t'ho penses com fer-ho per montar un cinema al poblat, un super i una cafeteria i al mateix temps no perdre les avantatges de la vida al lloc. Tots veiem el desastre que estem a punt de provocar. Simplement no funciona. Si vas allà, moltes coses que aquí son valors importants perden part del seu valor o simplement desapareixen. O bé ens adaptem a la forma de viure d'allà, ens esforcem per entendre per que fan el que fan, o bé estem comdenats a patir, més que gaudir, el nou lloc. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Alguna cosa semblat he vist a l'hora de resoldre un problema amb Hibernate. El procés mental seguit era calcular l'SQL que resol el problema i passar-ho el més semblant possible a Hibernate. Llavors sortien comentaris com "ostres, mira, resulta que s'ho menja", o "ei, sembla que això, encara que no ho entengui, o passi directament a l'SQL generat". &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Aquestes coses, tard o prest s'acaben pagant. Pot ser que els natius t'aguantin les excentricitats un cert temps, però no ho faran sempre. Amb Hibernate 3 els locals s'han posat més estrictes ... però siguem positius. Segur que ara, perfectament integrats, podrem gaudir plenament de la nova vida.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-8692006922232149531?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/8692006922232149531/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=8692006922232149531' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/8692006922232149531'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/8692006922232149531'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2009/03/hibernate-3.html' title='Hibernate 3'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-7819003860139514992</id><published>2008-12-13T19:10:00.000+01:00</published><updated>2008-12-13T19:12:34.229+01:00</updated><title type='text'>Teles, URLs ...</title><content type='html'>&lt;div&gt;Imagina que no saps com funciona una tele. Però res, ni la més remota idea. Ve un tècnic, te la monta, la posa en marxa per veure que funciona i, mentres tu la mires hipnotitzat el tècnic diu "molt bé, ja està. M'en vaig". &lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;La mires estona, tot satisfet fins que te canses i vols anar a dormir. Però llavors ningú t'ha dit que se pot apagar i mires com fer-ho per que el renou no te molesti. Així que la tapes amb un munt de mantes que redueixen el renou. Va bé, pots dormir ja que gairebé no se sent. L'endemà ensumes una olor rara degut a la la tele s'ha encalentit massa. Umm, se pot arreglar afegint un ventilador a la part del darrera. Perfecte. Però ara és massa feina llevar-ho tot en voler mirar la tele i montar-ho quan acabem. Així que feim lloc dins l'estructura per poder-hi ficar-nos, encara que això fa que haguem de treure un parell de mobles de la sala. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Només surt un inconvenient més quan un amic ens conte que hi ha més d'un canal, però ampliant l'habitacle per que hi càpiguen 8 teles més tot se resol (per el camí hem tombat dues parets i passat la cuina i el rebost a compartir espai amb el dormitori). Però la tele la podem veure. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;A vegades, aquests tipus d'estructures les trobem en els nostres programes només per el fet de no emprar les coses de la forma en que estan pensades. Veiem bocins de codi estranyíssim que l'únic que fan és tapar defectes del codi anterior que, per cert, no saps per que és allà. Línies i línies de codi van desaparaguent com la brutor quan passes l'aspiradora. L'efecte final, al manco quan ho faig jo, és "umm, no pot ser mai que això pugui ser així. Segur que el codi feia més coses que no he estat capaç de veure".&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Pot haver-hi moltes coses que originin aquestes criatures. Un cas freqüent és reinventar la roda pel fet de no mirar utilitats que ja existeixen. Per exemple, no emprar el taglib fmt per mostrar dates als JSP, o fer coses raríssimes que se resolen emprant DecimalFormat.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Tornat a les situacions originades per un ús incorrecte de la tecnologia, un exemple. Un cas al que li tenc una tirria especial és a la invenció de tècniques pròpies per l'enviament de paràmetres amb múltiples valors en una petició HTML. L'especificació està preparada per això. Tant en la seva codificació en les URLs o en el cos del POST, com en la API dels Servlets per recollir-ho. Però a vegades trobes codi que no, per alguna raó creu que és més fàcil construir dinàmicament noms únics de paràmetres. Llavors tot el codi dels servlets ja ha de fer coses estranyes per recuperar-los. O que m'en dieu de les solucions on s'envien tots els valors en un únic paràmetre emprant un caràcter especial com a separador? Uuufff. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;O pot ser estigui equivocat... és més fàcil tapar la tele amb una manta que cridar a un amic per que ens digui com emprar-la.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-7819003860139514992?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/7819003860139514992/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=7819003860139514992' title='4 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/7819003860139514992'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/7819003860139514992'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2008/12/teles-urls.html' title='Teles, URLs ...'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-1575447088667980065</id><published>2008-11-17T19:01:00.000+01:00</published><updated>2008-11-17T19:04:22.829+01:00</updated><title type='text'>Entities</title><content type='html'>Ja fa uns mesos vaig escriure una entrada parlant d’una possible implementació del mètode equals que es comportés correctament amb els casos d’herència&lt;br /&gt;&lt;br /&gt;D’ençà d’allò he donat moltes voltes al tema de l’herència, les diferències entre value objects i entities,  implicacions en la implementació, en el modelat, etc. He escrit bastants d’esborranys però el tema s’anava fent molt gros i, ara que no hi cap en una entrada normal, no sé que fer-ne. De fet, encara tenc un munt de notes que vaig escriguent en estar al tren, que no estan desenvolupades.&lt;br /&gt;&lt;br /&gt;Ahir estava pensant en les dificultats que me trovaba a l’hora de tractar correctament l’herència en els value objects així que vaig cercar refugi en les classes que no són value objects, les anomenades entitats ( hi ha altres tipus de classes, per exemple serveis, però a l’hora de representar “coses” les dues candidates naturals són entities i value objects ).&lt;br /&gt;&lt;br /&gt;Per tant, vaig considerar el cas plantejat per un dels meus revisors oficials. Suposem que tenim una classe Persona, amb nom i un mètode respira. Podem tenir subclasses com per exemple Psicoleg i Informàtic que herenden els seus membres sense cap problema afegit. L’equals heretat també hauria de funcionar correctament.&lt;br /&gt;&lt;br /&gt;Un altre exemple. Una classe Inmoble, amb un atribut adreça i subclasses Vivenda i Oficina. També tot trivial.&lt;br /&gt;&lt;br /&gt;Exemples perfectes de relació d’herencia o es-un: Una oficina és un immoble, una vivienda és un immoble. Informàtics i psicòlegs són persones.&lt;br /&gt;&lt;br /&gt;Ara bé, una persona pot ser informàtic i psicòleg. Si hem de poder representar això, hem de tenir una classe híbrida InformaticIPsicoleg o bé renunciar a l’herència. Qué ha fallat?&lt;br /&gt;&lt;br /&gt;No ho tinc gaire clar. Crec que l’arrel del problema està en el nostre llenguatge. La nostra capacitat de pensar està limitada per les paraules que manejem. El verb ser inclou molts de significats diferents, i a l’hora de identificar una relació com a relació es-un mesclem ous amb caragols.&lt;br /&gt;&lt;br /&gt;Idò, tenim Persona i hem de incorporar les distintes professions. Podem observar que la professió no forma una classificació de les persones sinó que més aviat és un atribut que les descriu.&lt;br /&gt;&lt;br /&gt;Podríem fer psicòleg i informàtic instàncies (no subclasses) de Professió i fer que una persona tingués com atribut una col•lecció de professions. La part estàtica queda ben resolte. Però només la part estàtica.&lt;br /&gt;&lt;br /&gt;Qué passa amb la part dinàmica? Una persona que és informàtica pot respirar (implementat a la definició de Persona) i pot escriure un HelloWorld. Com implementem això? L’herència, proposada a la primera solució, funciona molt millor que aquesta darrera proposta.&lt;br /&gt;&lt;br /&gt;La professió d’una persona no és un atribut “normal”, sinó que afecta al possible comportament de la persona. Una forma de representar això és fer Professio una super-interfícia. Aleshores Informatic i Psicoleg serien sub-interficies de Professio, cada un amb els seus mètodes ( implementaHelloWorld i psicoanalitza ).&lt;br /&gt;&lt;br /&gt;D’aquesta forma ja podem incorporar perfectament el polimorfisme al nostre model. Una persona que es informàtica, disposarà del mètode implementaHelloWorld. A més una persona podrà ser al mateix temps Psicoleg i Informatic. En aquest cas, a més de ser persona tindrà les capacitats especificades a les dues interfícies.&lt;br /&gt;&lt;br /&gt;Solucionat? Noooo. Això funcionaria si, en temps d’execució, afegíssim implementacions als objectes de la classe persona. Se pot fer, per exemple amb els “introductions” de l’Aspect Oriented Programming, però no forma part del conjunt de recursos de l’orientacio a objectes “plana”.&lt;br /&gt;&lt;br /&gt;A més, on coloquem la implementació de, per exemple, el mètode psicoanalitza? A Persona? No, només ho fan els psicolegs. A Psicoleg? No, és una interfície. Xungo.&lt;br /&gt;&lt;br /&gt;Aquest problema apareix en molts d’altres casos. L’altre exemple posat (Inmobles i les subclasses Vivenda i Oficina) té els mateixos problemes. En aquest cas, estem parlant de l’ús que donem al inmoble. Seria molt semblant, amb tots els seus problemes, a l’exemple de les professions.&lt;br /&gt;&lt;br /&gt;Alguna idea de com solucionar-ho sense incorporar aspectes o introspecció?&lt;br /&gt;&lt;br /&gt;Continuarà (esper)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-1575447088667980065?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/1575447088667980065/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=1575447088667980065' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/1575447088667980065'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/1575447088667980065'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2008/11/entities.html' title='Entities'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-268664507143952996</id><published>2008-10-23T21:14:00.001+02:00</published><updated>2008-10-24T23:24:20.564+02:00</updated><title type='text'>Primeres passes en Groovy</title><content type='html'>Ja fa un temps que estic llegint material sobre Groovy i Grails.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Com sol ser habitual amb mi, la part pràctica va molt per darrera de la teòrica així que he decidit  "embrutarme un poc les mans" amb el tema.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Ahir capvespre vaig fer una petita eina per tractar un dels aspectes pels que tenc una inclinació especial: la detecció de codi mort. No sé, manies meves.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;En concret, el programa Groovy inspecciona tots els fitxer hbm.xml i extreu totes les consultes declarades. Després analitza tots els fitxers Java i cerca l'ús que fa de les consultes. Cada consulta emprada és eliminada de la llista. Així, al final, queden les consultes que no són emprades en cap lloc del programa.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Me n'han sortit ... moltes !!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Hem té impresionat economia d'aquest llenguatge. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Mostro el codi, encara que sé que tindrà molts de defectes (és lent, pot no detectar alguns casos ...) i segur que encara és més "javero" que "groovià". Però paciència .... no és fa tot el camí amb un dia.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;class CercaConsultesNoEmprades {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;    Set consultes = new HashSet()&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;    Set javas = new HashSet()&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;    void extreuConsultesIFitxers(File file) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;        print '.'&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;        if (file.directory) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;            if (file.name != '.svn')&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;                file.eachFile {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;                     subdirectori -&gt; extreuConsultesIFitxers(subdirectori)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;                }&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;        } else {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;            if (file.name.endsWith('.hbm.xml')) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;                extreuConsultes file&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;            } else if (file.name.endsWith('.java')) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;                javas.add file&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;            }&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;        }&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;    }&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;    void cercaConsultesNoEmprades(File arrel) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;        println 'processant fitxers'&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;        extreuConsultesIFitxers(arrel)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;        println """&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;         trobades ${consultes.size()} consultes&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;         trobats ${javas.size()} fitxers Java&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;         cercant consultes no emprades&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;        """&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;        analitza()&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;    }&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;    void extreuConsultes(File file) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;        def hbm = new XmlParser().parse(file)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;        def queries = hbm.query&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;        for (Node query: queries) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;            consultes.add query.'@name'&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;        }&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;    }&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;    void analitza() {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;        for (File java : javas) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;            java.eachLine {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;                String linia -&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;                def trobada = null&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;                for (String consulta: consultes) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;                    if (linia.contains("\"" + consulta + "\"")) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;                        trobada = consulta&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;                        break&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;                    }&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;                }&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;                if (trobada != null) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;                    consultes.remove(trobada)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;                    print "."&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;                }&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;            }&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;        }&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;        println """&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;           trobades ${consultes.size()} no emprades&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;           guardant a consultesNoEmprades.txt &lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;           les consultes no emprades&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;        """&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;        File resultatConsultes = new File('consultesNoEmprades.txt')&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;        for (String consulta: consultes) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;            resultatConsultes &lt;&lt; consulta + '\n'&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;        }&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;    }&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;def directori = '/treball/educacio/gestibweb/modules/logica'&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;new CercaConsultesNoEmprades().cercaConsultesNoEmprades(new File(directori))&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-268664507143952996?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/268664507143952996/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=268664507143952996' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/268664507143952996'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/268664507143952996'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2008/10/primeres-passes-en-groovy.html' title='Primeres passes en Groovy'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-3417219987185815783</id><published>2008-10-16T18:33:00.000+02:00</published><updated>2008-10-16T18:47:56.726+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='eines'/><title type='text'>avisos</title><content type='html'>&lt;div&gt;Estic pensant amb penjar aquest cartell al darrera meu ...&lt;/div&gt;&lt;div&gt;( Si voleu jugar &lt;a href="http://www.addletters.com/warning-sign-generator.htm"&gt;voltros &lt;/a&gt;) &lt;/div&gt;&lt;div&gt;vist &lt;a href="http://caballe.cat/wp/2008/10/16/degenerador-de-senyals-davis/"&gt;aquí&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_QuRu-g0MTQM/SPduv7-TYSI/AAAAAAAADNQ/hC5AE5c2f3A/s1600-h/noms.gif"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_QuRu-g0MTQM/SPduv7-TYSI/AAAAAAAADNQ/hC5AE5c2f3A/s320/noms.gif" border="0" alt="" id="BLOGGER_PHOTO_ID_5257792859810259234" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_QuRu-g0MTQM/SPduYUecfyI/AAAAAAAADNA/YcyS4fzdnLo/s1600-h/mode_comu.gif"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_QuRu-g0MTQM/SPduYUecfyI/AAAAAAAADNA/YcyS4fzdnLo/s320/mode_comu.gif" border="0" alt="" id="BLOGGER_PHOTO_ID_5257792454070664994" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_QuRu-g0MTQM/SPduYoiSEpI/AAAAAAAADNI/f3ar7P6JY1s/s1600-h/no_tocar.gif"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_QuRu-g0MTQM/SPduYoiSEpI/AAAAAAAADNI/f3ar7P6JY1s/s320/no_tocar.gif" border="0" alt="" id="BLOGGER_PHOTO_ID_5257792459455468178" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_QuRu-g0MTQM/SPduInePNdI/AAAAAAAADMg/zChpDYffmn0/s1600-h/recursos.gif"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_QuRu-g0MTQM/SPduInePNdI/AAAAAAAADMg/zChpDYffmn0/s320/recursos.gif" border="0" alt="" id="BLOGGER_PHOTO_ID_5257792184292160978" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_QuRu-g0MTQM/SPduIuKLCLI/AAAAAAAADMo/NFCHLFExsgY/s1600-h/refactoritza.gif"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_QuRu-g0MTQM/SPduIuKLCLI/AAAAAAAADMo/NFCHLFExsgY/s320/refactoritza.gif" border="0" alt="" id="BLOGGER_PHOTO_ID_5257792186087049394" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_QuRu-g0MTQM/SPduIysYsSI/AAAAAAAADMw/AO5Yoh7yxOs/s1600-h/system_out.gif"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_QuRu-g0MTQM/SPduIysYsSI/AAAAAAAADMw/AO5Yoh7yxOs/s320/system_out.gif" border="0" alt="" id="BLOGGER_PHOTO_ID_5257792187304292642" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_QuRu-g0MTQM/SPduI8KhYCI/AAAAAAAADM4/gXPNRUlsvFo/s1600-h/trac.gif"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_QuRu-g0MTQM/SPduI8KhYCI/AAAAAAAADM4/gXPNRUlsvFo/s320/trac.gif" border="0" alt="" id="BLOGGER_PHOTO_ID_5257792189846609954" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_QuRu-g0MTQM/SPdt4w5XGxI/AAAAAAAADL4/7Hq9rT4kY_4/s1600-h/bbdd.gif"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_QuRu-g0MTQM/SPdt4w5XGxI/AAAAAAAADL4/7Hq9rT4kY_4/s320/bbdd.gif" border="0" alt="" id="BLOGGER_PHOTO_ID_5257791911943936786" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_QuRu-g0MTQM/SPdt5P0Mh4I/AAAAAAAADMA/bcr7OR5NEmU/s1600-h/commits.gif"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_QuRu-g0MTQM/SPdt5P0Mh4I/AAAAAAAADMA/bcr7OR5NEmU/s320/commits.gif" border="0" alt="" id="BLOGGER_PHOTO_ID_5257791920243771266" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_QuRu-g0MTQM/SPdt5Nuxp5I/AAAAAAAADMI/f145c9shUAQ/s1600-h/linies.gif"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_QuRu-g0MTQM/SPdt5Nuxp5I/AAAAAAAADMI/f145c9shUAQ/s320/linies.gif" border="0" alt="" id="BLOGGER_PHOTO_ID_5257791919684167570" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_QuRu-g0MTQM/SPdt5W7F0NI/AAAAAAAADMQ/2lCVGJl326s/s1600-h/mort.gif"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_QuRu-g0MTQM/SPdt5W7F0NI/AAAAAAAADMQ/2lCVGJl326s/s320/mort.gif" border="0" alt="" id="BLOGGER_PHOTO_ID_5257791922151739602" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_QuRu-g0MTQM/SPdt5U_3VvI/AAAAAAAADMY/opE8Ot_3a-A/s1600-h/proves.gif"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_QuRu-g0MTQM/SPdt5U_3VvI/AAAAAAAADMY/opE8Ot_3a-A/s320/proves.gif" border="0" alt="" id="BLOGGER_PHOTO_ID_5257791921634891506" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-3417219987185815783?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/3417219987185815783/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=3417219987185815783' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/3417219987185815783'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/3417219987185815783'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2008/10/avisos.html' title='avisos'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_QuRu-g0MTQM/SPduv7-TYSI/AAAAAAAADNQ/hC5AE5c2f3A/s72-c/noms.gif' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-2265274565470549856</id><published>2008-10-11T21:56:00.000+02:00</published><updated>2008-10-11T22:43:21.298+02:00</updated><title type='text'>jo client</title><content type='html'>Dijous passat vaig tenir l'experiència de participar a una reunió entre tècnics i clients per el desenvolupament d'un projecte informàtic. Res de nou, excepte que aquesta vegada jo era un client.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Va ser una experiència interessant. He de reconèixer que vaig estar una mica embullat, donada la novetat de la situació. Tampoc conec massa el negoci pel qual s'ha de fer l'aplicació, ja que el gestiona el meu germà.&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Ens van mostrar un esborrany en paper i molt simplificat de la idea que tenien per la web. Molt senzilla, visualment espectacular, i molt centrada en resoldre la funcionalitat bàsica. La idea me va agradar molt.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Clar que la idea, bastant nova per webs d'aquest estil, te tot el que impliquen les novetats: el seu risc.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Si apostes per una idea forta, has de ser coherent. Si el 90% de les altres webs han optat per una estructura bàsicament idèntica, és per que aquesta soluciona més o manco bé una sèrie habitual de problemes. Si tu l'abandones, ho has de fer acceptant les conseqüències. Si no mantens forta la "visió" del nou sistema, i contínuament vols ficar aspectes de la solució "tradicional", acabes amb un engendre que té els defectes de totes dues parts.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Aquest risc el vaig notar quan el meu germà anava responguent a les preguntes:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-style: italic;"&gt;Què és important?&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-style: italic;"&gt;Tot&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-style: italic;"&gt;Com és de important?&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-style: italic;"&gt;al màxim&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;Evidentment, no era així la conversació, però en alguns moments va semblar-s'hi perillosament.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Una altra cosa que vaig observar, estant al "lado oscuro", és que la gent sol confiar més en la seva opinió que amb la dels experts.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Si estudis mostren la importància de tenir unes opcions de cerca bàsica per disponibilitat a la primera pantalla, posa-les, encara que tú no creguis que sigui tan important. Per que, i això és una altra, no és cert que els usuaris tinguin Internet per connectar-se només a la teva web. No els hi sabrà gens de greu anar-s'en a una altra.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Després de observar una mica la l'esquema general i el seu funcionament amb distintes virgueries: multitud de fotos, flash a la presentació, ajax per un tubo ... me va sorgir una inquietud que tenc sempre en aquests casos: podrem funcionar SENSE tot això? M'agrada quan puc desactivar imatges, javascript, css, flash i tota la tropa i la web segueix operativa. Trobo que és una marca de qualitat. A més, no és només caprici, no soc expert en el tema però segur que hi ha avantatges en quan a normes d'accessibilitat o ampliació dels dispositius des dels que se pot accedir.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Encara que això darrer sigui un plus, on no soc flexible és en la necessitat de poder emprar els bookmarks, el back i el next. L'ús (o abús) de l'ajax no ho ha de posar en perill. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Tot plegat me fa pensar que pot ser ja sigui massa vell i senti enyorança de la web 1.0 ;-)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;No puc dir molt més. Mitjan reunió va sortir un tema tècnic que ells plantejaven com un repte a resoldre i pensant'hi (havia llegit alguna cosa semblant al GWT in Action) vaig perdre de vista la resta de la reunió :-(&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;És sol dir que els metges són els pitjors pacients. Serà també que els informàtics som els pitjors clients?&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-2265274565470549856?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/2265274565470549856/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=2265274565470549856' title='2 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/2265274565470549856'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/2265274565470549856'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2008/10/jo-client.html' title='jo client'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-7940654897098135521</id><published>2008-10-08T03:37:00.000+02:00</published><updated>2008-10-07T18:39:59.211+02:00</updated><title type='text'>GTD</title><content type='html'>Sempre he tengut el defecte de tendir poc a l'acció. Vull dir que me trobo molt més còmode analitzant els problemes i moguent-me en l'abstracció que no dins del compromis d'haver de realitzar accions concretes.&lt;br /&gt;&lt;br /&gt;Totes dues parts son necessàries, però és un gran inconvenient estar massa esbiaxat cap a un dels dos costats.&lt;br /&gt;&lt;br /&gt;Fa un temps vaig llegir el Getting Things Done. Un llibre clàssic dels que podriem etiquetar com de "desenvolupament personal" (de fet té precisament aquesta etiqueta, "desenvolupament personal", així que si les etiquetes són úniques, el que he dit abans és fals). No soc gaire donat a n'aquests tipus de llibres, però aquest era barat, i semblava tenir molta acceptació.&lt;br /&gt;&lt;br /&gt;M'ha agradat molt, encara que me costarà molt introduir-ho en els meus hàbits. Una cosa és tenir agradables sensacions d'estar anticipant millores mentres llegeixes el llibre, i una altra molt diferent cambiar les conductes tan arralades dins d'un mateix.&lt;br /&gt;&lt;br /&gt;Al meu entendre, la metodologia desenvolupada per l'autor es basa en una idea central.&lt;br /&gt;&lt;br /&gt;En primer lloc (per justificar-la), observa que la sensació d'estrés i baixa de productivitat ve del fet de tenir tot un conjunt de feines mal definides de les que ens sentim responsables. Una tasca no només ocupa el temps que esteim per solucionar-la. Tot el temps que roman indefinida, encara que no hi facem res, ocupa recursos psicològics en forma de preocupacions, distraccions i dificultats per relaxar-nos. A més tasques en aquest estat, menor és la nostra eficiència i, per tant, més tendència tenim a acumular "coses" que s'han de fer. El llibre afirma que treure totes aquestes coses ("stuff") del nivell semi-conscient i fer-ho explícit en una llista de coses per fer, fa que ens alliberem de la sensació descrita anteriorment. Això sí, ens alliberem en convertir en rutina anar realitzant tots els elements de la llista.&lt;br /&gt;&lt;br /&gt;Ara bé, i aquesta és la idea-guia del llibre, aquesta llista de coses a fer no pot estar de qualsevol manera. Suposo que la majoria dels 4 lectors del blog han experimentat llistes de coses per fer que no avançaven en absolut durant setmanes. L'autor diu que la llista serà útil si cada entrada representa una acció física, observable i discreta en el temps.&lt;br /&gt;&lt;br /&gt;Res d'entrades tipus: millorar la meva condició física, estar més amb els meus fills etc.&lt;br /&gt;Sí a entrades tipus: divendres a les 19:30 anar a correr 30 minuts. El dia abans cridar a XX per si vol venir. Dimarts a les 18:00 anar a passetjar una hora amb la meva filla.&lt;br /&gt;&lt;br /&gt;No m'estendre més amb el llibre ja que dos dels 4 lectors me esbroncaran, però sí que trobo que la idea s'ha de tenir en compte en les nostres tasques diàries. Exemple:&lt;br /&gt;&lt;br /&gt;Una entrada al trac que digui: "millorar la qualitat del codi dels serveis". Quan podrem dir que està fet? Què he de fer exactament? Podem tenir un mòdul ("projecte", al llibre) que sigui "control de qualitat" però les accions concretes han de ser això "accions" i "concretes".&lt;br /&gt;&lt;br /&gt;I que m'en dieu de les reunions on tot són temes i aportacions interessants, però on no es concreta res?. Succeeix que per acabar la discusió i passar a un altre tema algú diu: "bé, ho deixem aquí. Tu, Domingo, faras aquesta part". Buf, el què? com?. Consti que no som, el que se diu "un buen entendedor" i necessito moltes paraules, per tant moltes vegades pot ser ben bé que la culpa sigui meva.&lt;br /&gt;&lt;br /&gt;Darrerament he començat a esforçar-me en mantenir una llista de coses per fer tal com recomana el llibre i vos puc dir amb gran exactitud tot el que havia de fer i no he fet.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-7940654897098135521?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/7940654897098135521/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=7940654897098135521' title='2 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/7940654897098135521'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/7940654897098135521'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2008/08/gtd.html' title='GTD'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-6825752717751606463</id><published>2008-08-23T08:33:00.000+02:00</published><updated>2008-08-23T09:01:29.300+02:00</updated><title type='text'>truc</title><content type='html'>Un breu comentari per rompre la inactivitat del bloc&lt;br /&gt;&lt;br /&gt;En els fitxers build de l'xml sol ser habitual anar posant distints missatges que surtin per consola. La comanda per fer-ho és trivial:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&amp;lt;echo&amp;gt;Missatge a mostrar&amp;lt;/echo&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Va bé per comprovar què s'executa, mostrar valors de les propietats, ajudar a localitzar errades etc.&lt;br /&gt;&lt;br /&gt;El (petit) problema és que aquests missatges surten a llocs que no haurien de sortir. Per exemple, executant l'ant des de l'IntelliJ o en els reports del cruisecontrol aquests missatges surten com a warnings. Això "embruta" el procés.&lt;br /&gt;&lt;br /&gt;Ho tenia així des de feia temps. La típica cosa que "algun dia" has de mirar, ja que tens 200 tasques més importants abans.&lt;br /&gt;&lt;br /&gt;No sé com, fa poc vaig acabar a la pàgina de echo del manual d'ant. Allà vaig veure que hi havia un paràmetre "level" que és corresponia al nivell del missatge.  level tenia valors dintre dels coneguts nivells dels logs (info, warning, debug ...). El nivell per defecte és warning!&lt;br /&gt;&lt;br /&gt;Clar, tot explicat. Canviem tots els echo's per:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&amp;lt;echo level="info"&amp;gt; contar la meva vida &amp;lt;/echo&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;A partir d'aquí, tota la brutor desapareixia.&lt;br /&gt;&lt;br /&gt;Te sents com després de rentar el cotxe. No és que ara tenguis un ferrari, però és més agradable.&lt;br /&gt;&lt;br /&gt;Ei! que poc eficient! tants de paràgrafs per descriure un paràmetre de echo! m'ho he de fer mirar ;-)&lt;br /&gt;&lt;br/&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-6825752717751606463?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/6825752717751606463/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=6825752717751606463' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/6825752717751606463'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/6825752717751606463'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2008/08/truc.html' title='truc'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-705441035211126522</id><published>2008-08-04T20:43:00.000+02:00</published><updated>2008-08-04T21:59:04.184+02:00</updated><title type='text'>Equals, equivalence and inheritance II</title><content type='html'>Que era tot el trui de l'entrada anterior?&lt;br /&gt;&lt;br /&gt;(estava en anglés per mostrar-ho al foro d'un llibre)&lt;br /&gt;&lt;br /&gt; Volia provar de fer una implementació d'equals que complis el contracte d'aquest mètode (principalment que sigui una relació d'equivalència) inclus amb la presència de subclasses.&lt;br /&gt;&lt;br /&gt;Anem a veure la importància d'aquest compliment.&lt;br /&gt;&lt;br /&gt;Al llibre Effective Java posa un exemple interessant. Tenim la classe Date que evidentment té un equals ben implementat.&lt;br /&gt;&lt;br /&gt;Ara bé, apareix la subclasse java.sql.Timestamp i esten la classe afegint un camp per els nanosegons. Ja l'hem feta, com podem comprovar amb aquest exemple:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;        Date date = new Date();&lt;br /&gt;        Timestamp timestamp = new Timestamp(date.getTime());&lt;br /&gt;        System.out.println(" date.equals(timestamp) = " + ( date.equals(timestamp)  ));&lt;br /&gt;        System.out.println(" timestamp.equals(date) = " + ( timestamp.equals(date)  ));&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;El resultat és:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;date.equals(timestamp) = true&lt;br /&gt;timestamp.equals(date) = false&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;greu no? més del que pareix. No podem emprar en una mateixa col·lecció elements dels dos tipus sense esperar efectes estranys.&lt;br /&gt;&lt;br /&gt;En aquest context s'ha de aplicar la frasse del llibre:&lt;br /&gt;&lt;br /&gt;"(...).There is no way to extend an instantiable class and add a value&lt;br /&gt;component while preserving the equals contract, unless you are willing&lt;br /&gt;to forgo the benefits of object-oriented abstraction."&lt;br /&gt;&lt;br /&gt;Per abstracció, es refereix al fet que no podem implementar al equals del Date res que suposi el coneixement de com funciona la classe Timestamp. La segona és més específica, la primera més abstracte.&lt;br /&gt;&lt;br /&gt;Penseu que en la implementació de Date no poden considerar totes les subclasses d'aquesta que s'arrivaran a fer.&lt;br /&gt;&lt;br /&gt;Com podem resoldre el problema?&lt;br /&gt;&lt;br /&gt;A l'entrada anterior havia preparat una solució, però tenia una errada greu. Aquesta entrada la soluciona, però hauré de païr una mica més el tema per estar segur que no té errades. També s'ha de pensar en com fer més agradable el codi, que s'ha espatllat una mica.&lt;br /&gt;&lt;br /&gt;L'estratègia és la següent (comparem dos objectes o1 i o2).&lt;br /&gt;&lt;br /&gt;Partim de la classe de l'objecte o1, (la implemetació inicial de l'equals). La idea és cercar la primera superclasse que tenen en comú. Mentres no la trobem anem fent super.equals (...), i comprovant que el que afegeix de nou aquesta classe, respecte la seva classe pare, té un valor que pot ser considerat null.&lt;br /&gt;&lt;br /&gt;En trobar la superclasse comú, comencem el camí de davallada, fins la classe que defineix l'objecte o2. En iniciar aquesta devallada, s'executaran les implementacions sobre el segon objecte, que anirà mirant que tots els components que no són a la primera classe tenen un valor equivalent a null.&lt;br /&gt;&lt;br /&gt;Esquematitzat:&lt;br /&gt;&lt;br /&gt;suposem que c1 ..cn són les propietat comunes relevants per la identitat,&lt;br /&gt;x1 .. xn són les propietats relevants per la identitat que té o1, però no o2&lt;br /&gt;y1 .. yn són les propietats relevants per la identitat que té o2, però no o1&lt;br /&gt;&lt;br /&gt;la implementació comença executant tots els equals de la jerarquia d'o1 fins la classe comuna mirant que totes les propietats xi siguin nules. Arrivada a la classe comuna mira que cada propietat ci sigui iguala als dos objectes. Després, s'inicia el recorregut per els equals d'o2 comprovant el valor null dels yi.&lt;br /&gt;&lt;br /&gt;Les proves es fan amb les classes&lt;br /&gt;Point (x,y)&lt;br /&gt;  ThreeDPoint (x,y,z) esten Point&lt;br /&gt;  ColorPoint (x,y,color) esten Point&lt;br /&gt;     ThreeDColorPoint (x,y,z,color) esten ColorPoint&lt;br /&gt;        SpecializedThreeDColorPoint (x,y,z,color) esten ColorPoint - no implementa equals ni canvia l'estat&lt;br /&gt;&lt;br /&gt;El codi:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;//Point.java:&lt;br /&gt;public class Point {&lt;br /&gt;&lt;br /&gt;    private final int x;&lt;br /&gt;    private final int y;&lt;br /&gt;&lt;br /&gt;    public Point(int x, int y) {&lt;br /&gt;        this.x = x;&lt;br /&gt;        this.y = y;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public int getX() { return x; }&lt;br /&gt;    public int getY() { return y; }&lt;br /&gt;&lt;br /&gt;    public boolean equals(Object o) {&lt;br /&gt;        return equals(o,false);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public boolean equals (Object o, boolean reversed) {&lt;br /&gt;        // null, superclass or other&lt;br /&gt;        if (!(o instanceof Point)) {&lt;br /&gt;            return false;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        final Point other = (Point) o;&lt;br /&gt;&lt;br /&gt;        // subclass?&lt;br /&gt;        if (!reversed &amp;amp;&amp;amp; ! Point.class.equals(o.getClass())) {&lt;br /&gt;            return other.equals(this, true);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        return equalsToPoint(other);&lt;br /&gt;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    protected boolean equalsToPoint(Point point) {&lt;br /&gt;        return (x == point.x &amp;amp;&amp;amp; y == point.y);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public int hashCode() {&lt;br /&gt;        return 31 * x + y;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public String toString() {&lt;br /&gt;        return "point(" + x + ',' + y + ')';&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;//ThreeDPoint.java&lt;br /&gt;public class ThreeDPoint extends Point {&lt;br /&gt;   &lt;br /&gt;    private int z;&lt;br /&gt;&lt;br /&gt;    public ThreeDPoint(int x, int y, int z) {&lt;br /&gt;        super(x, y);&lt;br /&gt;        this.z = z;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public boolean equals(Object o) {&lt;br /&gt;        return equals(o,false);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public boolean equals (Object o, boolean reversed) {&lt;br /&gt;        // null, superclass or other&lt;br /&gt;        if (!(o instanceof ThreeDPoint)) {&lt;br /&gt;            return z==0 &amp;amp;&amp;amp; super.equals(o,reversed);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        final ThreeDPoint other = (ThreeDPoint) o;&lt;br /&gt;&lt;br /&gt;        // subclass?&lt;br /&gt;        if (!reversed &amp;amp;&amp;amp; ! ThreeDPoint.class.equals(o.getClass())) {&lt;br /&gt;            return other.equals(this, true);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        return equalsToThreeDPoint(other);&lt;br /&gt;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    protected boolean equalsToThreeDPoint(ThreeDPoint threeDPoint) {&lt;br /&gt;        if (!equalsToPoint(threeDPoint)) return false;&lt;br /&gt;        return z == threeDPoint.z;&lt;br /&gt;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public int hashCode() {&lt;br /&gt;        int result = super.hashCode();&lt;br /&gt;&lt;br /&gt;        // z value of 0 should not affect hashCode&lt;br /&gt;        if (z != 0) {&lt;br /&gt;            result = 31 * result + z;&lt;br /&gt;        }&lt;br /&gt;        return result;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public String toString() {&lt;br /&gt;        return "3DPoint(" + getX() + ',' + getY() + ',' +  z + ')';&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;//ColorPoint.java&lt;br /&gt;public class ColorPoint extends Point {&lt;br /&gt;&lt;br /&gt;    private final Color color;&lt;br /&gt;&lt;br /&gt;    public ColorPoint(int x, int y, Color color) {&lt;br /&gt;        super(x, y);&lt;br /&gt;        this.color = color;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public Color getColor() {&lt;br /&gt;        return color;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public boolean equals(Object o) {&lt;br /&gt;        return equals(o,false);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public boolean equals (Object o, boolean reversed) {&lt;br /&gt;        // null, superclass or other&lt;br /&gt;        if ( !(o instanceof ColorPoint)) {&lt;br /&gt;            return color == null &amp;amp;&amp;amp; super.equals(o,reversed); // could be equals&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        final ColorPoint other = (ColorPoint) o;&lt;br /&gt;&lt;br /&gt;        // subclass?&lt;br /&gt;        if (!reversed &amp;amp;&amp;amp; ! ColorPoint.class.equals(o.getClass())) {&lt;br /&gt;            return other.equals(this, true);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        return equalsToColorPoint(other);&lt;br /&gt;&lt;br /&gt;    }&lt;br /&gt;   &lt;br /&gt;    protected boolean equalsToColorPoint(ColorPoint colorPoint) {&lt;br /&gt;        if (!equalsToPoint(colorPoint)) return false;&lt;br /&gt;&lt;br /&gt;        if (color == null) return colorPoint.color == null;&lt;br /&gt;        return color.equals(colorPoint.color);&lt;br /&gt;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public int hashCode() {&lt;br /&gt;        int result = super.hashCode();&lt;br /&gt;        // color null should not affect hashCode&lt;br /&gt;        if (color != null) {&lt;br /&gt;            result =  result + 31 * color.hashCode();&lt;br /&gt;        }&lt;br /&gt;        return result;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    static Map&amp;lt;Color,String&amp;gt; colors = new HashMap&amp;lt;Color,String&amp;gt;(3);&lt;br /&gt;    static {&lt;br /&gt;        colors.put(Color.GREEN, "G");&lt;br /&gt;        colors.put(Color.RED, "R");&lt;br /&gt;        colors.put(Color.BLUE, "B");&lt;br /&gt;    }&lt;br /&gt;    public String colorToString(Color color) {&lt;br /&gt;        return  colors.get(color);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public String toString() {&lt;br /&gt;        return "colorPoint(" + getX() + ',' + getY() + ',' + colorToString(color) + ')';&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;//ThreeDColorPoint.java&lt;br /&gt;public class ThreeDColorPoint extends ColorPoint {&lt;br /&gt;&lt;br /&gt;    private final int z;&lt;br /&gt;&lt;br /&gt;    public ThreeDColorPoint(int x, int y, int z, Color color) {&lt;br /&gt;        super(x, y, color);&lt;br /&gt;        this.z = z;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public int getZ() {&lt;br /&gt;        return z;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public boolean equals(Object o) {&lt;br /&gt;        return equals(o,false);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public boolean equals (Object o, boolean reversed) {&lt;br /&gt;        // null, superclass or other&lt;br /&gt;        if (!(o instanceof ThreeDColorPoint)) {&lt;br /&gt;            return z==0 &amp;amp;&amp;amp; super.equals(o,reversed);&lt;br /&gt;        }&lt;br /&gt; &lt;br /&gt;        final ThreeDColorPoint other = (ThreeDColorPoint) o;&lt;br /&gt;&lt;br /&gt;        // subclass?&lt;br /&gt;        if (!reversed &amp;amp;&amp;amp; ! ThreeDColorPoint.class.equals(o.getClass())) {&lt;br /&gt;            return other.equals(this, true);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        return equalsToThreeDColorPoint(other);&lt;br /&gt;&lt;br /&gt;    }&lt;br /&gt; &lt;br /&gt;    protected boolean equalsToThreeDColorPoint(ThreeDColorPoint threeDColorPoint) {&lt;br /&gt;        if (!equalsToColorPoint(threeDColorPoint)) return false;&lt;br /&gt;        return z == threeDColorPoint.z;&lt;br /&gt;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public int hashCode() {&lt;br /&gt;        int result = super.hashCode();&lt;br /&gt;&lt;br /&gt;        // z value of 0 should not affect hashCode&lt;br /&gt;        if (z != 0) {&lt;br /&gt;            result = 31 * result + z;&lt;br /&gt;        }&lt;br /&gt;        return result;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public String toString() {&lt;br /&gt;        return "3DcolorPoint(" + getX() + ',' + getY() + ',' +  z + ',' + colorToString(getColor()) + ')';&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;//SpecializedThreeDColorPoint.java&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * change only behaviour&lt;br /&gt; */&lt;br /&gt;public class SpecializedThreeDColorPoint extends ThreeDColorPoint {&lt;br /&gt;&lt;br /&gt;    public SpecializedThreeDColorPoint(int x, int y, int z, Color color) {&lt;br /&gt;        super(x, y, z, color);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // does not overwrite equals&lt;br /&gt;&lt;br /&gt;    public void newMethod() {}&lt;br /&gt;&lt;br /&gt;    public String toString() {&lt;br /&gt;        return "specialized " + super.toString();&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;// Main.java amb les proves&lt;br /&gt;public class Main {&lt;br /&gt;&lt;br /&gt;    private static final boolean DEBUG = true;&lt;br /&gt;&lt;br /&gt;    public static void main(String[] args) {&lt;br /&gt;&lt;br /&gt;        // test with null&lt;br /&gt;        assertNotSame(new Point (1,2),null);&lt;br /&gt;        assertNotSame(new ColorPoint(1,2, Color.RED),null);&lt;br /&gt;        assertNotSame(new ThreeDColorPoint(1,2,3, Color.GREEN),null);&lt;br /&gt;        assertNotSame(new ThreeDColorPoint(1,2,3, Color.GREEN),"Other class");&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;        Point[][] equivalences = {&lt;br /&gt;                {new Point (1,2), new ColorPoint(1,2,null), new ThreeDColorPoint(1,2,0,null),new ThreeDPoint(1,2,0)},&lt;br /&gt;                {new Point(1,3)},&lt;br /&gt;                {new ColorPoint(1,4, Color.RED), new ThreeDColorPoint(1,4,0, Color.RED), new ThreeDColorPoint(1,4,0, Color.RED)},&lt;br /&gt;                {new ThreeDColorPoint(1,2,3, Color.RED)},&lt;br /&gt;                {new ThreeDColorPoint(1,2,0, Color.RED)},&lt;br /&gt;                {new ThreeDColorPoint(1,2,3, Color.GREEN) , new SpecializedThreeDColorPoint(1,2,3,Color.GREEN)}&lt;br /&gt;        };&lt;br /&gt;&lt;br /&gt;        for (int rowIndex = 0; rowIndex &lt; equivalences.length; rowIndex++) {&lt;br /&gt;            Point[] points = equivalences[rowIndex];&lt;br /&gt;            // points from same equivalence are equals&lt;br /&gt;            for (int j = 0; j &lt; points.length; j++) {&lt;br /&gt;                Point point = points[j];&lt;br /&gt;                for (int k = 0; k &lt; points.length; k++) {&lt;br /&gt;                    assertSame(point, points[k]);&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;            // points from other equivalences are not equals&lt;br /&gt;            for (int otherRowIndex = 0; otherRowIndex &lt; equivalences.length; otherRowIndex++) {&lt;br /&gt;                if (rowIndex == otherRowIndex) continue;&lt;br /&gt;                Point[] otherRow = equivalences[otherRowIndex];&lt;br /&gt;&lt;br /&gt;                for (int i = 0; i &lt; points.length; i++) {&lt;br /&gt;                    Point point = points[i];&lt;br /&gt;                    for (int j = 0; j &lt; otherRow.length; j++) {&lt;br /&gt;                        assertNotSame(point, otherRow[j]);&lt;br /&gt;                    }&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private static void assertSame(Object o1, Object o2) {&lt;br /&gt;        if (DEBUG) {&lt;br /&gt;            System.out.println("assertSame(" + o1 + ',' + o2 + ')');&lt;br /&gt;        }&lt;br /&gt;        assert o1.equals(o2): o1 + "!=" + o2;&lt;br /&gt;        if (o2 != null) {&lt;br /&gt;            assert o1.hashCode() == o2.hashCode() : o1 + ".hashCode() != " + o2 + ".hashCode()";&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private static void assertNotSame(Object o1, Object o2) {&lt;br /&gt;        if (DEBUG) {&lt;br /&gt;            System.out.println("assertNotSame(" + o1 + ',' + o2 + ')');&lt;br /&gt;        }&lt;br /&gt;        assert !o1.equals(o2): o1 + "==" + o2;&lt;br /&gt;        if (o2 != null) {&lt;br /&gt;            assert o1.hashCode() != o2.hashCode() : o1 + ".hashCode() == " + o2 + ".hashCode()";&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;El resultat d'executar això és:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;assertNotSame(point(1,2),null)&lt;br /&gt;assertNotSame(colorPoint(1,2,R),null)&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,3,G),null)&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,3,G),Other class)&lt;br /&gt;assertSame(point(1,2),point(1,2))&lt;br /&gt;assertSame(point(1,2),colorPoint(1,2,null))&lt;br /&gt;assertSame(point(1,2),3DcolorPoint(1,2,0,null))&lt;br /&gt;assertSame(point(1,2),3DPoint(1,2,0))&lt;br /&gt;assertSame(colorPoint(1,2,null),point(1,2))&lt;br /&gt;assertSame(colorPoint(1,2,null),colorPoint(1,2,null))&lt;br /&gt;assertSame(colorPoint(1,2,null),3DcolorPoint(1,2,0,null))&lt;br /&gt;assertSame(colorPoint(1,2,null),3DPoint(1,2,0))&lt;br /&gt;assertSame(3DcolorPoint(1,2,0,null),point(1,2))&lt;br /&gt;assertSame(3DcolorPoint(1,2,0,null),colorPoint(1,2,null))&lt;br /&gt;assertSame(3DcolorPoint(1,2,0,null),3DcolorPoint(1,2,0,null))&lt;br /&gt;assertSame(3DcolorPoint(1,2,0,null),3DPoint(1,2,0))&lt;br /&gt;assertSame(3DPoint(1,2,0),point(1,2))&lt;br /&gt;assertSame(3DPoint(1,2,0),colorPoint(1,2,null))&lt;br /&gt;assertSame(3DPoint(1,2,0),3DcolorPoint(1,2,0,null))&lt;br /&gt;assertSame(3DPoint(1,2,0),3DPoint(1,2,0))&lt;br /&gt;assertNotSame(point(1,2),point(1,3))&lt;br /&gt;assertNotSame(colorPoint(1,2,null),point(1,3))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,0,null),point(1,3))&lt;br /&gt;assertNotSame(3DPoint(1,2,0),point(1,3))&lt;br /&gt;assertNotSame(point(1,2),colorPoint(1,4,R))&lt;br /&gt;assertNotSame(point(1,2),3DcolorPoint(1,4,0,R))&lt;br /&gt;assertNotSame(point(1,2),3DcolorPoint(1,4,0,R))&lt;br /&gt;assertNotSame(colorPoint(1,2,null),colorPoint(1,4,R))&lt;br /&gt;assertNotSame(colorPoint(1,2,null),3DcolorPoint(1,4,0,R))&lt;br /&gt;assertNotSame(colorPoint(1,2,null),3DcolorPoint(1,4,0,R))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,0,null),colorPoint(1,4,R))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,0,null),3DcolorPoint(1,4,0,R))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,0,null),3DcolorPoint(1,4,0,R))&lt;br /&gt;assertNotSame(3DPoint(1,2,0),colorPoint(1,4,R))&lt;br /&gt;assertNotSame(3DPoint(1,2,0),3DcolorPoint(1,4,0,R))&lt;br /&gt;assertNotSame(3DPoint(1,2,0),3DcolorPoint(1,4,0,R))&lt;br /&gt;assertNotSame(point(1,2),3DcolorPoint(1,2,3,R))&lt;br /&gt;assertNotSame(colorPoint(1,2,null),3DcolorPoint(1,2,3,R))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,0,null),3DcolorPoint(1,2,3,R))&lt;br /&gt;assertNotSame(3DPoint(1,2,0),3DcolorPoint(1,2,3,R))&lt;br /&gt;assertNotSame(point(1,2),3DcolorPoint(1,2,0,R))&lt;br /&gt;assertNotSame(colorPoint(1,2,null),3DcolorPoint(1,2,0,R))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,0,null),3DcolorPoint(1,2,0,R))&lt;br /&gt;assertNotSame(3DPoint(1,2,0),3DcolorPoint(1,2,0,R))&lt;br /&gt;assertNotSame(point(1,2),3DcolorPoint(1,2,3,G))&lt;br /&gt;assertNotSame(point(1,2),specialized 3DcolorPoint(1,2,3,G))&lt;br /&gt;assertNotSame(colorPoint(1,2,null),3DcolorPoint(1,2,3,G))&lt;br /&gt;assertNotSame(colorPoint(1,2,null),specialized 3DcolorPoint(1,2,3,G))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,0,null),3DcolorPoint(1,2,3,G))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,0,null),specialized 3DcolorPoint(1,2,3,G))&lt;br /&gt;assertNotSame(3DPoint(1,2,0),3DcolorPoint(1,2,3,G))&lt;br /&gt;assertNotSame(3DPoint(1,2,0),specialized 3DcolorPoint(1,2,3,G))&lt;br /&gt;assertSame(point(1,3),point(1,3))&lt;br /&gt;assertNotSame(point(1,3),point(1,2))&lt;br /&gt;assertNotSame(point(1,3),colorPoint(1,2,null))&lt;br /&gt;assertNotSame(point(1,3),3DcolorPoint(1,2,0,null))&lt;br /&gt;assertNotSame(point(1,3),3DPoint(1,2,0))&lt;br /&gt;assertNotSame(point(1,3),colorPoint(1,4,R))&lt;br /&gt;assertNotSame(point(1,3),3DcolorPoint(1,4,0,R))&lt;br /&gt;assertNotSame(point(1,3),3DcolorPoint(1,4,0,R))&lt;br /&gt;assertNotSame(point(1,3),3DcolorPoint(1,2,3,R))&lt;br /&gt;assertNotSame(point(1,3),3DcolorPoint(1,2,0,R))&lt;br /&gt;assertNotSame(point(1,3),3DcolorPoint(1,2,3,G))&lt;br /&gt;assertNotSame(point(1,3),specialized 3DcolorPoint(1,2,3,G))&lt;br /&gt;assertSame(colorPoint(1,4,R),colorPoint(1,4,R))&lt;br /&gt;assertSame(colorPoint(1,4,R),3DcolorPoint(1,4,0,R))&lt;br /&gt;assertSame(colorPoint(1,4,R),3DcolorPoint(1,4,0,R))&lt;br /&gt;assertSame(3DcolorPoint(1,4,0,R),colorPoint(1,4,R))&lt;br /&gt;assertSame(3DcolorPoint(1,4,0,R),3DcolorPoint(1,4,0,R))&lt;br /&gt;assertSame(3DcolorPoint(1,4,0,R),3DcolorPoint(1,4,0,R))&lt;br /&gt;assertSame(3DcolorPoint(1,4,0,R),colorPoint(1,4,R))&lt;br /&gt;assertSame(3DcolorPoint(1,4,0,R),3DcolorPoint(1,4,0,R))&lt;br /&gt;assertSame(3DcolorPoint(1,4,0,R),3DcolorPoint(1,4,0,R))&lt;br /&gt;assertNotSame(colorPoint(1,4,R),point(1,2))&lt;br /&gt;assertNotSame(colorPoint(1,4,R),colorPoint(1,2,null))&lt;br /&gt;assertNotSame(colorPoint(1,4,R),3DcolorPoint(1,2,0,null))&lt;br /&gt;assertNotSame(colorPoint(1,4,R),3DPoint(1,2,0))&lt;br /&gt;assertNotSame(3DcolorPoint(1,4,0,R),point(1,2))&lt;br /&gt;assertNotSame(3DcolorPoint(1,4,0,R),colorPoint(1,2,null))&lt;br /&gt;assertNotSame(3DcolorPoint(1,4,0,R),3DcolorPoint(1,2,0,null))&lt;br /&gt;assertNotSame(3DcolorPoint(1,4,0,R),3DPoint(1,2,0))&lt;br /&gt;assertNotSame(3DcolorPoint(1,4,0,R),point(1,2))&lt;br /&gt;assertNotSame(3DcolorPoint(1,4,0,R),colorPoint(1,2,null))&lt;br /&gt;assertNotSame(3DcolorPoint(1,4,0,R),3DcolorPoint(1,2,0,null))&lt;br /&gt;assertNotSame(3DcolorPoint(1,4,0,R),3DPoint(1,2,0))&lt;br /&gt;assertNotSame(colorPoint(1,4,R),point(1,3))&lt;br /&gt;assertNotSame(3DcolorPoint(1,4,0,R),point(1,3))&lt;br /&gt;assertNotSame(3DcolorPoint(1,4,0,R),point(1,3))&lt;br /&gt;assertNotSame(colorPoint(1,4,R),3DcolorPoint(1,2,3,R))&lt;br /&gt;assertNotSame(3DcolorPoint(1,4,0,R),3DcolorPoint(1,2,3,R))&lt;br /&gt;assertNotSame(3DcolorPoint(1,4,0,R),3DcolorPoint(1,2,3,R))&lt;br /&gt;assertNotSame(colorPoint(1,4,R),3DcolorPoint(1,2,0,R))&lt;br /&gt;assertNotSame(3DcolorPoint(1,4,0,R),3DcolorPoint(1,2,0,R))&lt;br /&gt;assertNotSame(3DcolorPoint(1,4,0,R),3DcolorPoint(1,2,0,R))&lt;br /&gt;assertNotSame(colorPoint(1,4,R),3DcolorPoint(1,2,3,G))&lt;br /&gt;assertNotSame(colorPoint(1,4,R),specialized 3DcolorPoint(1,2,3,G))&lt;br /&gt;assertNotSame(3DcolorPoint(1,4,0,R),3DcolorPoint(1,2,3,G))&lt;br /&gt;assertNotSame(3DcolorPoint(1,4,0,R),specialized 3DcolorPoint(1,2,3,G))&lt;br /&gt;assertNotSame(3DcolorPoint(1,4,0,R),3DcolorPoint(1,2,3,G))&lt;br /&gt;assertNotSame(3DcolorPoint(1,4,0,R),specialized 3DcolorPoint(1,2,3,G))&lt;br /&gt;assertSame(3DcolorPoint(1,2,3,R),3DcolorPoint(1,2,3,R))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,3,R),point(1,2))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,3,R),colorPoint(1,2,null))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,3,R),3DcolorPoint(1,2,0,null))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,3,R),3DPoint(1,2,0))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,3,R),point(1,3))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,3,R),colorPoint(1,4,R))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,3,R),3DcolorPoint(1,4,0,R))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,3,R),3DcolorPoint(1,4,0,R))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,3,R),3DcolorPoint(1,2,0,R))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,3,R),3DcolorPoint(1,2,3,G))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,3,R),specialized 3DcolorPoint(1,2,3,G))&lt;br /&gt;assertSame(3DcolorPoint(1,2,0,R),3DcolorPoint(1,2,0,R))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,0,R),point(1,2))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,0,R),colorPoint(1,2,null))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,0,R),3DcolorPoint(1,2,0,null))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,0,R),3DPoint(1,2,0))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,0,R),point(1,3))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,0,R),colorPoint(1,4,R))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,0,R),3DcolorPoint(1,4,0,R))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,0,R),3DcolorPoint(1,4,0,R))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,0,R),3DcolorPoint(1,2,3,R))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,0,R),3DcolorPoint(1,2,3,G))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,0,R),specialized 3DcolorPoint(1,2,3,G))&lt;br /&gt;assertSame(3DcolorPoint(1,2,3,G),3DcolorPoint(1,2,3,G))&lt;br /&gt;assertSame(3DcolorPoint(1,2,3,G),specialized 3DcolorPoint(1,2,3,G))&lt;br /&gt;assertSame(specialized 3DcolorPoint(1,2,3,G),3DcolorPoint(1,2,3,G))&lt;br /&gt;assertSame(specialized 3DcolorPoint(1,2,3,G),specialized 3DcolorPoint(1,2,3,G))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,3,G),point(1,2))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,3,G),colorPoint(1,2,null))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,3,G),3DcolorPoint(1,2,0,null))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,3,G),3DPoint(1,2,0))&lt;br /&gt;assertNotSame(specialized 3DcolorPoint(1,2,3,G),point(1,2))&lt;br /&gt;assertNotSame(specialized 3DcolorPoint(1,2,3,G),colorPoint(1,2,null))&lt;br /&gt;assertNotSame(specialized 3DcolorPoint(1,2,3,G),3DcolorPoint(1,2,0,null))&lt;br /&gt;assertNotSame(specialized 3DcolorPoint(1,2,3,G),3DPoint(1,2,0))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,3,G),point(1,3))&lt;br /&gt;assertNotSame(specialized 3DcolorPoint(1,2,3,G),point(1,3))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,3,G),colorPoint(1,4,R))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,3,G),3DcolorPoint(1,4,0,R))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,3,G),3DcolorPoint(1,4,0,R))&lt;br /&gt;assertNotSame(specialized 3DcolorPoint(1,2,3,G),colorPoint(1,4,R))&lt;br /&gt;assertNotSame(specialized 3DcolorPoint(1,2,3,G),3DcolorPoint(1,4,0,R))&lt;br /&gt;assertNotSame(specialized 3DcolorPoint(1,2,3,G),3DcolorPoint(1,4,0,R))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,3,G),3DcolorPoint(1,2,3,R))&lt;br /&gt;assertNotSame(specialized 3DcolorPoint(1,2,3,G),3DcolorPoint(1,2,3,R))&lt;br /&gt;assertNotSame(3DcolorPoint(1,2,3,G),3DcolorPoint(1,2,0,R))&lt;br /&gt;assertNotSame(specialized 3DcolorPoint(1,2,3,G),3DcolorPoint(1,2,0,R))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Agrairia comentaris si algú li troba algún defecte a aquesta implementació.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-705441035211126522?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/705441035211126522/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=705441035211126522' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/705441035211126522'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/705441035211126522'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2008/08/equals-equivalence-and-inheritance-ii.html' title='Equals, equivalence and inheritance II'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-2519451000380862301</id><published>2008-08-02T18:45:00.000+02:00</published><updated>2008-08-04T21:50:59.319+02:00</updated><title type='text'>Equals, equivalence and inheritance</title><content type='html'>&lt;span style="font-weight: bold;"&gt;** EDITED **&lt;br /&gt;code need to be revised&lt;br /&gt;some corrections made in &lt;a href="http://domingosebastian.blogspot.com/2008/08/equals-equivalence-and-inheritance-ii.html"&gt;http://domingosebastian.blogspot.com/2008/08/equals-equivalence-and-inheritance-ii.html&lt;/a&gt;&lt;br /&gt;************&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;From Joshua Bloch's Effective Java book:&lt;br /&gt;&lt;br /&gt;"(...).There is no way to extend an instantiable class and add a value&lt;br /&gt;component while preserving the equals contract, unless you are willing&lt;br /&gt;to forgo the benefits of object-oriented abstraction."&lt;br /&gt;&lt;br /&gt;I think that is not correct, as I will try to prove:&lt;br /&gt;&lt;br /&gt;The general structure of an equals method should be:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;** EDITED **&lt;br /&gt;code need to be revised&lt;br /&gt;some corrections made in &lt;a href="http://domingosebastian.blogspot.com/2008/08/equals-equivalence-and-inheritance-ii.html"&gt;http://domingosebastian.blogspot.com/2008/08/equals-equivalence-and-inheritance-ii.html&lt;/a&gt;&lt;br /&gt;************&lt;/span&gt;&lt;pre&gt;&lt;br /&gt; public boolean equals(Object o) {&lt;br /&gt;       // null, superclass or other&lt;br /&gt;       if (!(o instanceof [MyClass])) {&lt;br /&gt;           return extraComponent== null &amp;amp;&amp;amp; super.equals(o); // if could be equals or return false if not&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       // subclass&lt;br /&gt;       if (! [MyClass].class.equals(o.getClass())) {&lt;br /&gt;            return o.equals(this); // let the more specific equals do the job (open door for inheritance)&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       // equality for this class&lt;br /&gt;       return equalsTo[MyClass](([MyClass) o);&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt; protected boolean equalsTo[MyClass]([MyClass] other) {&lt;br /&gt;     ...&lt;br /&gt; }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Let test this method with the following classes (also from Joshua's book):&lt;br /&gt;A Point is a 2-dimensional point (x,y)&lt;br /&gt;A ColorPoint extends Point with a color (x, y, color)&lt;br /&gt;A ThreeDColorPoint extends ColorPoint with another dimension ( x,y, z, color)&lt;br /&gt;&lt;br /&gt;two points are equals if their non zero dimension are equals and if&lt;br /&gt;the color is the same or null (more clearly explained in the code).&lt;br /&gt;&lt;br /&gt;Code for each class:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;** EDITED **&lt;br /&gt;code need to be revised&lt;br /&gt;some corrections made in &lt;a href="http://domingosebastian.blogspot.com/2008/08/equals-equivalence-and-inheritance-ii.html"&gt;http://domingosebastian.blogspot.com/2008/08/equals-equivalence-and-inheritance-ii.html&lt;/a&gt;&lt;br /&gt;************&lt;/span&gt;&lt;pre&gt;&lt;br /&gt;// Point.java&lt;br /&gt;public class Point {&lt;br /&gt;&lt;br /&gt;   private final int x;&lt;br /&gt;   private final int y;&lt;br /&gt;&lt;br /&gt;   public Point(int x, int y) {&lt;br /&gt;       this.x = x;&lt;br /&gt;       this.y = y;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public int getX() {&lt;br /&gt;       return x;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public int getY() {&lt;br /&gt;       return y;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public boolean equals(Object o) {&lt;br /&gt;&lt;br /&gt;       // null, superclass or other&lt;br /&gt;       if (!(o instanceof Point)) return false;  // always false&lt;br /&gt;&lt;br /&gt;       // subclass?&lt;br /&gt;       if (!Point.class.equals(o.&lt;wbr&gt;getClass())) {&lt;br /&gt;           return o.equals(this);&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       // same class&lt;br /&gt;       return equalsToPoint((Point) o);&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   protected boolean equalsToPoint(Point point) {&lt;br /&gt;       if (x != point.x) return false;&lt;br /&gt;       if (y != point.y) return false;&lt;br /&gt;&lt;br /&gt;       return true;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public int hashCode() {&lt;br /&gt;       int result;&lt;br /&gt;       result = x;&lt;br /&gt;       result = 31 * result + y;&lt;br /&gt;       return result;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public String toString() {&lt;br /&gt;       return "point(" + x + ',' + y + ')';&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// ColorPoint.java&lt;br /&gt;public class ColorPoint extends Point {&lt;br /&gt;&lt;br /&gt;   private final Color color;&lt;br /&gt;&lt;br /&gt;   public ColorPoint(int x, int y, Color color) {&lt;br /&gt;       super(x, y);&lt;br /&gt;       this.color = color;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public Color getColor() {&lt;br /&gt;       return color;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public boolean equals(Object o) {&lt;br /&gt;&lt;br /&gt;       // null, superclass or other&lt;br /&gt;       if (!(o instanceof ColorPoint)) {&lt;br /&gt;           return color == null &amp;amp;&amp;amp; super.equals(o); // could be equals&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       // subclass?&lt;br /&gt;       if (! ColorPoint.class.equals(o.&lt;wbr&gt;getClass())) {&lt;br /&gt;           return o.equals(this);&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       return equalsToColorPoint((&lt;wbr&gt;ColorPoint) o);&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   protected boolean equalsToColorPoint(ColorPoint colorPoint) {&lt;br /&gt;       if (!equalsToPoint(colorPoint)) return false;&lt;br /&gt;&lt;br /&gt;       if (color == null) return colorPoint.color == null;&lt;br /&gt;       return color.equals(colorPoint.color)&lt;wbr&gt;;&lt;br /&gt;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public int hashCode() {&lt;br /&gt;       int result = super.hashCode();&lt;br /&gt;       // color null should not affect hashCode&lt;br /&gt;       if (color != null) {&lt;br /&gt;           result =  result + 31 * color.hashCode();&lt;br /&gt;       }&lt;br /&gt;       return result;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public String toString() {&lt;br /&gt;       return "colorPoint(" + getX() + ',' + getY() + ',' + color + ')';&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;//ThreeDColorPoint&lt;br /&gt;public class ThreeDColorPoint extends ColorPoint {&lt;br /&gt;&lt;br /&gt;   private final int z;&lt;br /&gt;&lt;br /&gt;   public ThreeDColorPoint(int x, int y, int z, Color color) {&lt;br /&gt;       super(x, y, color);&lt;br /&gt;       this.z = z;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public int getZ() {&lt;br /&gt;       return z;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public boolean equals(Object o) {&lt;br /&gt;&lt;br /&gt;       // null, superclass or other&lt;br /&gt;       if (!(o instanceof ThreeDColorPoint)) {&lt;br /&gt;           return z == 0 &amp;amp;&amp;amp; super.equals(o); // could be equals&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       // subclass?&lt;br /&gt;       if (!ThreeDColorPoint.class.&lt;wbr&gt;equals(o.getClass())) {&lt;br /&gt;           return o.equals(this);&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       return equalsToThreeDColorPoint((&lt;wbr&gt;ThreeDColorPoint) o);&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   protected boolean equalsToThreeDColorPoint(&lt;wbr&gt;ThreeDColorPoint threeDColorPoint) {&lt;br /&gt;       if (!equalsToColorPoint(&lt;wbr&gt;threeDColorPoint)) return false;&lt;br /&gt;       return z == threeDColorPoint.z;&lt;br /&gt;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public int hashCode() {&lt;br /&gt;       int result = super.hashCode();&lt;br /&gt;&lt;br /&gt;       // z value of 0 should not affect hashCode&lt;br /&gt;       if (z != 0) {&lt;br /&gt;           result = 31 * result + z;&lt;br /&gt;       }&lt;br /&gt;       return result;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public String toString() {&lt;br /&gt;       return "3DcolorPoint(" + getX() + ',' + getY() + ',' +  z + ',' + getColor() + ')';&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;We can check equivalence is honoured with this test class:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class Main {&lt;br /&gt;&lt;br /&gt;   private static final boolean DEBUG = true;&lt;br /&gt;&lt;br /&gt;   public static void main(String[] args) {&lt;br /&gt;&lt;br /&gt;       // test with null&lt;br /&gt;       assertNotSame(new Point (1,2),null);&lt;br /&gt;       assertNotSame(new ColorPoint(1,2, Color.RED),null);&lt;br /&gt;       assertNotSame(new ThreeDColorPoint(1,2,3, Color.GREEN),null);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;       // each row is a different equivalence class&lt;br /&gt;       Point[][] equivalences = {&lt;br /&gt;               {new Point (1,2), new ColorPoint(1,2,null), new ThreeDColorPoint(1,2,0,null)},&lt;br /&gt;               {new Point(1,3)},&lt;br /&gt;               {new ColorPoint(1,4, Color.RED), new ThreeDColorPoint(1,4,0, Color.RED), new ThreeDColorPoint(1,4,0,Color.RED)},&lt;br /&gt;               {new ThreeDColorPoint(1,2,3, Color.RED)},&lt;br /&gt;               {new ThreeDColorPoint(1,2,3, Color.GREEN)}&lt;br /&gt;       };&lt;br /&gt;&lt;br /&gt;       for (int rowIndex = 0; rowIndex &lt; points =" equivalences[rowIndex];" j =" 0;" point =" points[j];" k =" 0;" otherrowindex =" 0;" rowindex ="="" otherrow =" equivalences[otherRowIndex];" i =" 0;" point =" points[i];" j =" 0;"&gt;assertSame(" + o1 + ',' + o2 + ')');&lt;br /&gt;       }&lt;br /&gt;       assert o1.equals(o2): o1 + "!=" + o2;&lt;br /&gt;       if (o2 != null) {&lt;br /&gt;           assert o1.hashCode() == o2.hashCode() : o1 + ".hashCode() != " + o2 + ".hashCode()";&lt;br /&gt;       }&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   private static void assertNotSame (Object o1, Object o2) {&lt;br /&gt;       if (DEBUG) {&lt;br /&gt;           System.out.println("&lt;wbr&gt;assertNotSame(" + o1 + ',' + o2 + ')');&lt;br /&gt;       }&lt;br /&gt;       assert !o1.equals(o2): o1 + "==" + o2;&lt;br /&gt;       if (o2 != null) {&lt;br /&gt;           assert o1.hashCode() != o2.hashCode() : o1 + ".hashCode() == " + o2 + ".hashCode()";&lt;br /&gt;       }&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-2519451000380862301?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/2519451000380862301/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=2519451000380862301' title='1 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/2519451000380862301'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/2519451000380862301'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2008/08/equals-equivalence-and-inheritance.html' title='Equals, equivalence and inheritance'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-4544231605343006479</id><published>2008-07-27T08:33:00.000+02:00</published><updated>2008-07-27T08:00:11.637+02:00</updated><title type='text'>Primer de subversion : branches i merge</title><content type='html'>&lt;span style="font-weight: bold;"&gt;Arrancada de cavall ...&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;Suposem que estem fent un projecte i ens acostem a la data de lliurement de la primera versió.&lt;br /&gt;&lt;br /&gt;Despleguem una versió al servidor de proves, els usuaris interns fan proves, se detecten errades petites que són corregides a l'instant i se dona per bo el producte. Fantàstic, enfornem una versió i cap a producció.&lt;br /&gt;&lt;br /&gt;Mentrestant comencem una altre iteració (o fita, milestone, sprint ...) prevista per d'aquí a tres setmanes. Se van fent commits amb la nova funcionalitat i petites millores a les coses ja existents. Tot va beníssim.&lt;br /&gt;&lt;br /&gt;Però ... surt una errada greu al sistema en producció. Cal corregir-la ràpidament. El codi per solucionar-ho és evident. Ho feim, provem una mica la nova versió i ho enviem. No hi ha temps de fer proves tan exhaustives com les fetes en la versió anterior i la nova funcionalitat s'envia com està. La correcció és urgent.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;... arrivada d'ase&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;Pas a producció. Desplega bé, l'errada s'ha corregida. Però el codi nou ha introduit dues errades més. Són bones de solucionar. De fet ja estan solucionades al codi més actual. Preparem una versió nova. Dos desenvolupadors diuen que no se pot fer, ja que el codi no és complet. Han de fer més commits, esperem que els facin i mirem com compila. Suposem que el codi nou és força senzill i no pot fallar (ui, ui quan pensem això...). Commit, construcció i cap a producció.&lt;br /&gt;&lt;br /&gt;Nova versió. Mateixos problemes. En cada versió el codi està menys provat i tot s'ha fet amb més urgència.&lt;br /&gt;&lt;br /&gt;Em entrat de ple en un cercle viciós. Cada versió és més inestable que l'anterior per estar menys provada. Errades obliguen a enviar ràpid, que obliga a reduir proves,  que produeixen noves errades i torna a començar.&lt;br /&gt;&lt;br /&gt;Anem a revisar el procés per veure que falla.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Rebobinant&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;La primera versió enviada tenia el codi ben provat, ja que ha estat la única que ha tingut unes proves d'acceptació tal com cal.&lt;br /&gt;&lt;br /&gt;Detectada la primera errada greu han començat els problemes. Introduit el codi que corregeix la errada, el nostre codi tenia dues característiques:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Tenia la correcció a l'errada que ens interessa&lt;/li&gt;&lt;li&gt;Tenia tot el codi de la nova versió, que encara no s'havia provat de forma adient.&lt;/li&gt;&lt;/ol&gt;En aquest punt és on ens hem quedat sense gaires opcions. O esperavem a acabar i comprovar bé aquesta versió, que podia ser un parell de setmanes, o ho enviavem amb proves insuficients. La gravetat de l'errada només permetia la segona opció.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;La idea&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Tornant al punt anterior, quina seria la solució més interessant? Sembla clar:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Disposar a producció de la versió estable, modificant només el codi per l'errada greu&lt;/li&gt;&lt;li&gt;Diferir els altres canvis per una propera versió més provada&lt;/li&gt;&lt;/ul&gt;És a dir, volem fer front a les errades inevitables de producció però mantenint la planificació de les noves iteracions.&lt;br /&gt;&lt;br /&gt;Això fa que necessitem dues versions del producte. Una de manteniment de la versió de producció, on és faran només canvis crítics, i una de desenvolupament, segons la planificació prevista.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;La implementació&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;Emprant el subversion la solució anterior la implementarem disposant de dues branches, dues versions del mateix producte que poden evolucionar de forma independent.&lt;br /&gt;&lt;br /&gt;Per possibilitar la solució, haurem de seguir l'estructura recomanada en els manuals introductoris de subversion. Això és: tenir com a carpetes de més alt nivell del nostre projecte les següents:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;trunk &lt;/span&gt;: amb el codi del nostre projecte&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;branches &lt;/span&gt;: guardarà versions diferents del nostre codi (versió de desenvolupament, versió de manteniment de bugs, etc. )&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;tags &lt;/span&gt;: guardarà "congelades" distintes releases que haguem fet (versió 1.0, versió 2.1 etc. )&lt;/li&gt;&lt;/ul&gt;Inicialment branches i tags quedaran buides i només treballarem amb trunk.&lt;br /&gt;&lt;br /&gt;Arrivat el moment de fer la primera release, farem un còpia del que hi ha a trunk a branches:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;svn copy url_repositori/projecte/trunk  url_repositori/projecte/branches/branch_1_0 -m "creant branca per versió 1.0"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;En aquest moment s'ha creat al servidor una còpia del projecte en l'estat actual. Damunt trunk podem seguir fent feina de forma normal, avançant cap a la següent versió. Aquests canvis no tenen cap afecte a la branca nova creada.&lt;br /&gt;&lt;br /&gt;Detectada l'errada a producció, prepararem una versió idèntica del que hi ha actualment, afegint-hi només la solució a aquest problema.&lt;br /&gt;&lt;br /&gt;Per això baixem una copia de la branca de la versió 1.0:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;svn co url_repositori/projecte/branches/branch_1_0  projecte_1.0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Corregirem el problema, fem commit, i enviarem una nova versió. Tot fàcil, no?&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;La complicació: el merge&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;Anem a entrar, en aquest tractament minimalista dels branches, en la part més delicada: els merge.&lt;br /&gt;&lt;br /&gt;Suposem que l'errada que hem trobat a producció també es dona a la línia actual de desenvolupament (trunk). En aquest cas voldrem aplicar el mateix canvi que hem fet al branch al trunk.&lt;br /&gt;&lt;br /&gt;Per això hem de dir "mira subversion, aquest canvi que he fet en aquest directori (per ell tot son directoris) vull que ho facis en aquest altre", amb el seu llenguatge, clar.&lt;br /&gt;&lt;br /&gt;Si el commit fet en el branch de la versió 1.0 ha creat la revisió 201 del projecte, per aplicar aquest canvi a trunk ens  situarem a la nostra còpia local de trunk (a on volem aplicar els canvis) i executarem:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;svn merge -r 200:201 url_repositori/projecte/branches/branch_1_0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;És a dir, aplica les diferencies que hi ha entre la versió 201 i 200 a branch, a la meva còpia local, en aquest cas, trunk. Llavors revisarem que tot funciona i, si és així, farem un commit.&lt;br /&gt;&lt;br /&gt;En cas de voler propagar els canvis d'un sol commit hi ha una forma més pràctica de fer-ho que és:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;svn merge -c 201 url_repositori/projecte/branches/branch_1_0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;La dificultat del merge no és tant entendre la sentència (que pot costar un poc al principi) com gestionar correctament que tots els canvis que interessen se passen una i només una vegada a cada branca i se integren bé amb els canvis ja fets en aquestes. Pensau que també ens pot interessar passar correccions fetes en el trunk cap a la branca de manteniment !&lt;br /&gt;&lt;br /&gt;Per això, la recomanació és fer lliuraments de versió freqüents i no prolongar massa temps les vides de les branques.&lt;span style="font-style: italic;"&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;Resumint&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Encara que les branques i els merge siguin uns dels aspectes més complexes de la nostra eina de control de versions, l'esforç que facem per entendre com funcionen és molt necessari.&lt;br /&gt;&lt;br /&gt;Ben emprades, aquestes eines en permetran respondre a bé a canvis urgents al temps que el desenvolupament normal de la nova versió no es veurà afectat. Així cada versió enviada a producció podrà passar per totes les fases de proves i controls de qualitat necessaris per lliurar-lo amb garanties.&lt;br /&gt;&lt;br /&gt;Com podeu imaginar, hi ha molt més per tractar aquí (per exemple, ni hem mencionat els tags), però la idea era fer-ho el més reduït possible.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-4544231605343006479?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/4544231605343006479/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=4544231605343006479' title='3 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/4544231605343006479'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/4544231605343006479'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2008/07/primer-de-subversion-branches-i-merge.html' title='Primer de subversion : branches i merge'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-1682013079802155474</id><published>2008-07-20T17:07:00.000+02:00</published><updated>2008-07-20T17:41:21.942+02:00</updated><title type='text'>Preparant les emergències</title><content type='html'>Anem a preparar-nos per quan les coses vagin malament.&lt;br /&gt;&lt;br /&gt;Facem un Servlet un tant “toca-nassos” amb el següent codi.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class ConsumidorConnexionsServlet  extends HttpServlet {&lt;br /&gt;&lt;br /&gt;    DataSource ds;&lt;br /&gt;&lt;br /&gt;    public void init() throws ServletException {&lt;br /&gt;        try {&lt;br /&gt;            Context ctx = new InitialContext();&lt;br /&gt;            ds = (DataSource) ctx.lookup("nom del datasource");&lt;br /&gt;        } catch (NamingException e) {&lt;br /&gt;            throw new ServletException("Problemes localitzant DS", e);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws&lt;br /&gt;                                  IOException, ServletException {&lt;br /&gt;&lt;br /&gt;        Connection con = null;&lt;br /&gt;        PreparedStatement pst = null;&lt;br /&gt;        try {&lt;br /&gt;            con = ds.getConnection();&lt;br /&gt;            pst = con.prepareStatement(".. una consulta ...");&lt;br /&gt;            ResultSet rst = pst.executeQuery();&lt;br /&gt;            final PrintWriter out = response.getWriter();&lt;br /&gt;            out.println("resultat");&lt;br /&gt;            while (rst.next()) {&lt;br /&gt;                out.println(rst.getString("una columna"));&lt;br /&gt;            }&lt;br /&gt;            rst.close();&lt;br /&gt;        } catch (SQLException e) {&lt;br /&gt;            throw new ServletException(e);&lt;br /&gt;        } finally {&lt;br /&gt;            if (pst != null) {&lt;br /&gt;                try {pst.close();} catch (Exception e) {}&lt;br /&gt;            }&lt;br /&gt;            if (con != null) {&lt;br /&gt;       //         try {con.close();} catch (Exception e) {}  COMENTAT!!&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Aquest Servlet té la fea costum d'agafar una connexió del pool de connexions i no tornar-la.&lt;br /&gt;&lt;br /&gt; Que passarà si algú executa aquest Servlet? [emprarem el JBoss-3.2.7-caib1]  En aparença res. El Servlet s'executarà correctament.&lt;br /&gt;Això sí, el pool de connexions tindrà una connexió menys, però no ens enfadarem per això, no?&lt;br /&gt;&lt;br /&gt;Posem que el pool té una màxim de 5 connexions. Ara són 4. Rebem una altre petició (3), i una altre 2, 1, 0. Ueps, que passa amb al següent? El codi se quedarà bloquejat a la línia :&lt;br /&gt;&lt;pre&gt;&lt;br /&gt; con = ds.getConnection();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ja que el pool no pot retornar més connexions, i la petició web quedarà penjada generant la resposta. Bé, no passa res ..., no ens estressem ..., la resta de l'aplicació va bé, ejem .... si no ha d'emprar la base de dades.&lt;br /&gt;&lt;br /&gt;Però, com si fos una epidèmia, l'enfermetat comença a ocupar més recursos. Tenim una petició web penjada, això implica que hi ha un thread per peticions http que està bloquejat.  Ara, cada nova petició web queda bloquejada (en intentar obtenir una connexió) i ocupant un nou thread. Aquests threads, organitzats també com un pool, s'arriven a esgotar. Ara tota l'aplicació web queda sense respondre. No hi ha activitat a la CPU, ni a la base de dades, ni logs al JBoss, però l'aplicació web no respon ... ummm, ens preocupem? Bé, al cap i a la fi, a ningú li depen la vida en aquesta aplicació, no?&lt;br /&gt;&lt;br /&gt;Fora broma, si això ens passa a producció, no estarem gaire relaxats. Fer-ho ara a ca nostra ens permet preparar-nos per aquestes situacions.&lt;br /&gt;&lt;br /&gt;En aquest cas, la situació s'arregla parcialment. El bloqueig iniciat al mètode getConnection() té associat un timeout i la petició acaba fallant superat aquest temps. El mètode acaba amb excepció (i un log amb informació de l'error!), s'allibera el thread per les peticions http, però el pool de connexions segueix esgotat.&lt;br /&gt;&lt;br /&gt;[nota: el comportament del pool (nombre màxim de connexions i timeout) el podem modificar emprant el jmx-console: jboss.jca -&gt; name=nomDS,service=ManagedConnectionPool ]&lt;br /&gt;&lt;br /&gt;[per carregar el servidor, podeu emprar una eina tipus l'apache benchmark, el JMeter o, si no, fer-vos una classe utilitat com aquesta:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class Executor {&lt;br /&gt;&lt;br /&gt;    public static void main(String[] args) {&lt;br /&gt;&lt;br /&gt;        int NOMBRE_THREADS = 20;&lt;br /&gt;&lt;br /&gt;        for (int i = 0 ; i &amp;lt; NOMBRE_THREADS; i++) {&lt;br /&gt;            new Thread (new Descarregador( i, "http://url_peticio")).start();&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;class Descarregador implements Runnable {&lt;br /&gt;&lt;br /&gt;    int numeroThread;&lt;br /&gt;    private String url;&lt;br /&gt;&lt;br /&gt;    Descarregador(int numeroThread, String url) {&lt;br /&gt;        this.numeroThread = numeroThread;&lt;br /&gt;        this.url = url;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void run() {&lt;br /&gt;        System.out.println("iniciant thread " + numeroThread);&lt;br /&gt;        URL url;&lt;br /&gt;        try {&lt;br /&gt;            url = new URL(this.url);&lt;br /&gt;        } catch (MalformedURLException e) {&lt;br /&gt;            throw new RuntimeException(e);&lt;br /&gt;        }&lt;br /&gt;        try {&lt;br /&gt;            BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));&lt;br /&gt;            String str;&lt;br /&gt;            while ((str = in.readLine()) != null) {&lt;br /&gt;                System.out.println(str);&lt;br /&gt;            }&lt;br /&gt;        } catch (IOException e) {&lt;br /&gt;            e.printStackTrace();&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;]&lt;br /&gt;Evidentment, reiniciar el servidor ens recuperarà el servei ... fins que torni a fallar.&lt;br /&gt;&lt;br /&gt;Podem fer res abans d'aturar el servidor per obtenir més informació? Una solució molt interessant és fer un dump de la màquina virtual. Per això a la cònsola feiem en windows Ctrl + despl i amb linux un kill -3 (necessitem capturar la sortida estàndard)&lt;br /&gt;&lt;br /&gt;Si ho feim ens sortirà un volcat de dades tan desagradable com, en aquest cas, útil.&lt;br /&gt;&lt;br /&gt;Trobarem informació per cada un dels threads i ens sortiran coses així:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;"http-localhost%2F127.0.0.1-8080-Processor13" daemon prio=6 tid=0x279ddfe0 nid=0xeb8 in Object.wait() [0x284ce000..0x284cfae8]&lt;br /&gt;        at java.lang.Object.wait(Native Method)&lt;br /&gt;        - waiting on &lt;0x02f964f8&gt; (a EDU.oswego.cs.dl.util.concurrent.QueuedSemaphore$WaitQueue$WaitNode)&lt;br /&gt;        at EDU.oswego.cs.dl.util.concurrent.QueuedSemaphore$WaitQueue$WaitNode.doTimedWait(QueuedSemaphore.java:123)&lt;br /&gt;        - locked &lt;0x02f964f8&gt; (a EDU.oswego.cs.dl.util.concurrent.QueuedSemaphore$WaitQueue$WaitNode)&lt;br /&gt;        at EDU.oswego.cs.dl.util.concurrent.QueuedSemaphore.attempt(QueuedSemaphore.java:47)&lt;br /&gt;        at org.jboss.resource.connectionmanager.InternalManagedConnectionPool.getConnection(InternalManagedConnectionPoo&lt;br /&gt;l.java:138)&lt;br /&gt;        at org.jboss.resource.connectionmanager.JBossManagedConnectionPool$BasePool.getConnection(JBossManagedConnection&lt;br /&gt;Pool.java:535)&lt;br /&gt;        at org.jboss.resource.connectionmanager.BaseConnectionManager2.getManagedConnection(BaseConnectionManager2.java:&lt;br /&gt;445)&lt;br /&gt;        at org.jboss.resource.connectionmanager.TxConnectionManager.getManagedConnection(TxConnectionManager.java:298)&lt;br /&gt;        at org.jboss.resource.connectionmanager.BaseConnectionManager2.allocateConnection(BaseConnectionManager2.java:49&lt;br /&gt;7)&lt;br /&gt;        at org.jboss.resource.connectionmanager.BaseConnectionManager2$ConnectionManagerProxy.allocateConnection(BaseCon&lt;br /&gt;nectionManager2.java:888)&lt;br /&gt;        at org.jboss.resource.adapter.jdbc.WrapperDataSource.getConnection(WrapperDataSource.java:102)&lt;br /&gt;        at prova.caiguda.ConsumidorConnexionsServlet.doGet(ConsumidorConnexionsServlet.java:38)&lt;br /&gt;        at javax.servlet.http.HttpServlet.service(HttpServlet.java:697)&lt;br /&gt;        at javax.servlet.http.HttpServlet.service(HttpServlet.java:810)&lt;br /&gt;        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:252)&lt;br /&gt;        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)&lt;br /&gt;        at org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:75)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;En aquesta cas es tracta d'un thread que processa peticions http i podem veure que està bloquejat al getConnection().&lt;br /&gt;&lt;br /&gt;Més coses.&lt;br /&gt;&lt;br /&gt;Hem vist que podem anar recollint informació del sistema en fallida per analitzar-ho (anàlisi pot-mortem). Però també podem actuar en la línia de prevenir aquestes coses, monitoritzant el funcionament del sistema.&lt;br /&gt;&lt;br /&gt;Podem fer un Servlet que ens mostri tots els threads i el seu estat. El codi que ho fa podria ser alguna cosa com aquesta (versió minimament modificada de &lt;a href="http://ehcache.sourceforge.net/xref-test/net/sf/ehcache/distribution/JVMUtil.html"&gt;això&lt;/a&gt;):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;   private void mostraThreads(PrintWriter out) {&lt;br /&gt;        ThreadGroup root = obtenirRoot();&lt;br /&gt;&lt;br /&gt;        visit(root, 0, out);&lt;br /&gt;     }&lt;br /&gt;&lt;br /&gt;    private ThreadGroup obtenirRoot() {&lt;br /&gt;        ThreadGroup root = Thread.currentThread().getThreadGroup().getParent();&lt;br /&gt;        while (root.getParent() != null) {&lt;br /&gt;          root = root.getParent();&lt;br /&gt;        }&lt;br /&gt;        return root;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    private void visit(ThreadGroup group, int level, PrintWriter out) {&lt;br /&gt;&lt;br /&gt;        // Get threads in 'group'&lt;br /&gt;        int numThreads = group.activeCount();&lt;br /&gt;        Thread[] threads = new Thread[numThreads*2];&lt;br /&gt;        numThreads = group.enumerate(threads, false);&lt;br /&gt;&lt;br /&gt;       &lt;br /&gt;        // Enumerate each thread in 'group'&lt;br /&gt;        for (int i=0; i&amp;lt;numThreads; i++) {&lt;br /&gt;            // Get thread&lt;br /&gt;            Thread thread = threads[i];&lt;br /&gt;            out.println(level + "  - " + thread.getName() + " - " + thread.getState());&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        // Get thread subgroups of 'group'&lt;br /&gt;        int numGroups = group.activeGroupCount();&lt;br /&gt;        ThreadGroup[] groups = new ThreadGroup[numGroups*2];&lt;br /&gt;        numGroups = group.enumerate(groups, false);&lt;br /&gt;&lt;br /&gt;        // Recursively visit each subgroup&lt;br /&gt;        for (int i=0; i&amp;lt;numGroups; i++) {&lt;br /&gt;            visit(groups[i], level+1, out);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;El resultat d'executar aquest Servlet és un informe com el que segueix:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;0  - Reference Handler - WAITING&lt;br /&gt;0  - Finalizer - WAITING&lt;br /&gt;0  - Signal Dispatcher - RUNNABLE&lt;br /&gt;0  - RMI TCP Accept-1098 - RUNNABLE&lt;br /&gt;0  - RMI Reaper - WAITING&lt;br /&gt;0  - GC Daemon - TIMED_WAITING&lt;br /&gt;0  - RMI TCP Accept-4444 - RUNNABLE&lt;br /&gt;1  - DestroyJavaVM - RUNNABLE&lt;br /&gt;2  - Timer-0 - TIMED_WAITING&lt;br /&gt;2  - ScannerThread - TIMED_WAITING&lt;br /&gt;2  - Timer-1 - WAITING&lt;br /&gt;2  - ContainerBackgroundProcessor[StandardEngine[jboss.web]] - TIMED_WAITING&lt;br /&gt;2  - HSQLDB Timer @152643 - TIMED_WAITING&lt;br /&gt;2  - JBossMQ Cache Reference Softner - TIMED_WAITING&lt;br /&gt;2  - Timer-2 - TIMED_WAITING&lt;br /&gt;2  - TimeoutFactory - WAITING&lt;br /&gt;2  - IdleRemover - TIMED_WAITING&lt;br /&gt;2  - JCA PoolFiller - WAITING&lt;br /&gt;2  - JBossLifeThread - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor1 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor2 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor3 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor4 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor5 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor6 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor7 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor8 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor9 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor10 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor11 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor12 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor13 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor14 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor15 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor16 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor17 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor18 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor19 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor20 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor21 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor22 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor23 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor24 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor25 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Monitor - TIMED_WAITING&lt;br /&gt;2  - TP-Processor1 - WAITING&lt;br /&gt;2  - TP-Processor2 - WAITING&lt;br /&gt;2  - TP-Processor3 - WAITING&lt;br /&gt;2  - TP-Processor4 - RUNNABLE&lt;br /&gt;2  - TP-Monitor - TIMED_WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8443-Processor1 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8443-Processor2 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8443-Processor3 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8443-Processor4 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8443-Processor5 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8443-Monitor - TIMED_WAITING&lt;br /&gt;3  - ClassLoadingPool(2)-1 - RUNNABLE&lt;br /&gt;4  - JBoss System Threads(1)-1 - RUNNABLE&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;voilà! Ja tenim una petita consola de monitorització.&lt;br /&gt;&lt;br /&gt;Anem a fer 10 peticions al servlet&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;0  - Reference Handler - WAITING&lt;br /&gt;0  - Finalizer - WAITING&lt;br /&gt;0  - Signal Dispatcher - RUNNABLE&lt;br /&gt;0  - RMI TCP Accept-1098 - RUNNABLE&lt;br /&gt;0  - RMI Reaper - WAITING&lt;br /&gt;0  - GC Daemon - TIMED_WAITING&lt;br /&gt;0  - RMI TCP Accept-4444 - RUNNABLE&lt;br /&gt;1  - DestroyJavaVM - RUNNABLE&lt;br /&gt;2  - Timer-0 - TIMED_WAITING&lt;br /&gt;2  - ScannerThread - TIMED_WAITING&lt;br /&gt;2  - Timer-1 - WAITING&lt;br /&gt;2  - ContainerBackgroundProcessor[StandardEngine[jboss.web]] - TIMED_WAITING&lt;br /&gt;2  - HSQLDB Timer @8321c8 - TIMED_WAITING&lt;br /&gt;2  - JBossMQ Cache Reference Softner - TIMED_WAITING&lt;br /&gt;2  - Timer-2 - TIMED_WAITING&lt;br /&gt;2  - TimeoutFactory - TIMED_WAITING&lt;br /&gt;2  - IdleRemover - TIMED_WAITING&lt;br /&gt;2  - JCA PoolFiller - TIMED_WAITING&lt;br /&gt;2  - JBossLifeThread - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor1 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor2 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor3 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor4 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor5 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor6 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor7 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor8 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor9 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor10 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor11 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor12 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor13 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor14 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor15 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor16 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor17 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor18 - TIMED_WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor19 - TIMED_WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor20 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor21 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor22 - TIMED_WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor23 - TIMED_WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor24 - TIMED_WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor25 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Monitor - TIMED_WAITING&lt;br /&gt;2  - TP-Processor1 - WAITING&lt;br /&gt;2  - TP-Processor2 - WAITING&lt;br /&gt;2  - TP-Processor3 - WAITING&lt;br /&gt;2  - TP-Processor4 - RUNNABLE&lt;br /&gt;2  - TP-Monitor - TIMED_WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8443-Processor1 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8443-Processor2 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8443-Processor3 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8443-Processor4 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8443-Processor5 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8443-Monitor - TIMED_WAITING&lt;br /&gt;3  - ClassLoadingPool(2)-1 - RUNNABLE&lt;br /&gt;4  - JBoss System Threads(1)-1 - RUNNABLE&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Veiem que hi ha 5 threads http-localhost en l'estat TIMED_WAITING (les 5 primeres peticions han obtingut connexió del pool i per tant s'han processat completament).&lt;br /&gt;&lt;br /&gt;Al cap d'una estona, els bloquejos del getConnection() arriven al timeout i tots els threads tornen estar preparats per atendre peticions.&lt;br /&gt;&lt;br /&gt;Canviem el Servlet per a que no se recuperi.&lt;br /&gt;&lt;br /&gt;Facem que el cos del mètode doGet sigui:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;while (true) {}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Facem 10 invocacions al Servlet i observem l'estat:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;0  - Reference Handler - WAITING&lt;br /&gt;0  - Finalizer - WAITING&lt;br /&gt;0  - Signal Dispatcher - RUNNABLE&lt;br /&gt;0  - RMI TCP Accept-1098 - RUNNABLE&lt;br /&gt;0  - RMI Reaper - WAITING&lt;br /&gt;0  - GC Daemon - TIMED_WAITING&lt;br /&gt;0  - RMI TCP Accept-4444 - RUNNABLE&lt;br /&gt;1  - DestroyJavaVM - RUNNABLE&lt;br /&gt;2  - Timer-0 - TIMED_WAITING&lt;br /&gt;2  - ScannerThread - TIMED_WAITING&lt;br /&gt;2  - Timer-1 - WAITING&lt;br /&gt;2  - ContainerBackgroundProcessor[StandardEngine[jboss.web]] - TIMED_WAITING&lt;br /&gt;2  - HSQLDB Timer @24eafa - TIMED_WAITING&lt;br /&gt;2  - JBossMQ Cache Reference Softner - TIMED_WAITING&lt;br /&gt;2  - Timer-2 - TIMED_WAITING&lt;br /&gt;2  - TimeoutFactory - TIMED_WAITING&lt;br /&gt;2  - IdleRemover - TIMED_WAITING&lt;br /&gt;2  - JCA PoolFiller - WAITING&lt;br /&gt;2  - JBossLifeThread - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor1 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor2 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor3 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor4 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor5 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor6 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor7 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor8 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor9 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor10 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor11 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor12 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor13 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor14 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor15 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor16 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor17 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor18 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor19 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor20 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor21 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor22 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor23 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor24 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor25 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Monitor - TIMED_WAITING&lt;br /&gt;2  - TP-Processor1 - WAITING&lt;br /&gt;2  - TP-Processor2 - WAITING&lt;br /&gt;2  - TP-Processor3 - WAITING&lt;br /&gt;2  - TP-Processor4 - RUNNABLE&lt;br /&gt;2  - TP-Monitor - TIMED_WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8443-Processor1 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8443-Processor2 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8443-Processor3 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8443-Processor4 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8443-Processor5 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8443-Monitor - TIMED_WAITING&lt;br /&gt;3  - ClassLoadingPool(2)-1 - RUNNABLE&lt;br /&gt;4  - JBoss System Threads(1)-1 - RUNNABLE&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Els threads que atenen (indefinidament) les peticions rebudes ara estan en l'estat RUNNABLE, és a dir, no hi ha cap bloqueig sinó que estan fent feina. De fet, en aquest cas l'ús de CPU pujarà cap el 100%.&lt;br /&gt;&lt;br /&gt;Observem que passa si afegim 20 peticions més (tenim 13 threads lliures):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;0  - Reference Handler - WAITING&lt;br /&gt;0  - Finalizer - WAITING&lt;br /&gt;0  - Signal Dispatcher - RUNNABLE&lt;br /&gt;0  - RMI TCP Accept-1098 - RUNNABLE&lt;br /&gt;0  - RMI Reaper - WAITING&lt;br /&gt;0  - GC Daemon - TIMED_WAITING&lt;br /&gt;0  - RMI TCP Accept-4444 - RUNNABLE&lt;br /&gt;1  - DestroyJavaVM - RUNNABLE&lt;br /&gt;2  - Timer-0 - TIMED_WAITING&lt;br /&gt;2  - ScannerThread - TIMED_WAITING&lt;br /&gt;2  - Timer-1 - WAITING&lt;br /&gt;2  - ContainerBackgroundProcessor[StandardEngine[jboss.web]] - TIMED_WAITING&lt;br /&gt;2  - HSQLDB Timer @152643 - TIMED_WAITING&lt;br /&gt;2  - JBossMQ Cache Reference Softner - TIMED_WAITING&lt;br /&gt;2  - Timer-2 - TIMED_WAITING&lt;br /&gt;2  - TimeoutFactory - TIMED_WAITING&lt;br /&gt;2  - IdleRemover - TIMED_WAITING&lt;br /&gt;2  - JCA PoolFiller - WAITING&lt;br /&gt;2  - JBossLifeThread - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor1 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor2 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor3 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor4 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor5 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor6 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor7 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor8 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor9 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor10 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor11 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor12 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor13 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor14 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor15 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor16 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor17 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor18 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor19 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor20 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor21 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor22 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor23 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor24 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor25 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Monitor - TIMED_WAITING&lt;br /&gt;2  - TP-Processor1 - WAITING&lt;br /&gt;2  - TP-Processor2 - WAITING&lt;br /&gt;2  - TP-Processor3 - WAITING&lt;br /&gt;2  - TP-Processor4 - RUNNABLE&lt;br /&gt;2  - TP-Monitor - TIMED_WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8443-Processor1 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8443-Processor2 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8443-Processor3 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8443-Processor4 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8443-Processor5 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8443-Monitor - TIMED_WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor26 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor27 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor28 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor29 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor30 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor31 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor32 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor33 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor34 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor35 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor36 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor37 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor38 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor39 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor40 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor41 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor42 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor43 - WAITING&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor44 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor45 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor46 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor47 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor48 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor49 - RUNNABLE&lt;br /&gt;2  - http-localhost%2F127.0.0.1-8080-Processor50 - RUNNABLE&lt;br /&gt;3  - ClassLoadingPool(2)-1 - RUNNABLE&lt;br /&gt;4  - JBoss System Threads(1)-1 - RUNNABLE&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;S'han creat 25 threads més per atendre peticions http. Aquest comportament és bo per adaptar el servidor a puntes de càrrega però en el nostre cas només està retrassant una mort inevitable.&lt;br /&gt;&lt;br /&gt;[L'estratègia en el manteniment del pool de threads http el podem configurar amb el jmx-console, al MBean jbossweb → name=http-localhost/127.0.0.1-8080,type=ThreadPool]&lt;br /&gt;&lt;br /&gt;Amb el servidor patint, també el recórrer al dump ens mostrarà la causa del nostre problema. Entre totes les entrades, les dels threads ocupats mostraran un stack trace semblant a aquest:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;"http-localhost%2F127.0.0.1-8080-Processor17" daemon prio=6 tid=0x27c8f818 nid=0x854 runnable [0x285cf000..0x285cfce8]&lt;br /&gt;        at prova.caiguda.ConsumidorConnexionsServlet.doGet(ConsumidorConnexionsServlet.java:35)&lt;br /&gt;        at javax.servlet.http.HttpServlet.service(HttpServlet.java:697)&lt;br /&gt;        at javax.servlet.http.HttpServlet.service(HttpServlet.java:810)&lt;br /&gt;        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:252)&lt;br /&gt;        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)&lt;br /&gt;        at org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:75)&lt;br /&gt;        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:202)&lt;br /&gt;        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)&lt;br /&gt;        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:214)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Si tenim el servidor “fregit” i abans d'aturar feiem aquest dump, serà bo de trobar el punt que s'ens està menjant tota la capacitat de procés.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-1682013079802155474?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/1682013079802155474/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=1682013079802155474' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/1682013079802155474'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/1682013079802155474'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2008/07/preparant-les-emergncies.html' title='Preparant les emergències'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-8070749349994836806</id><published>2008-07-19T09:28:00.000+02:00</published><updated>2008-07-19T09:49:46.589+02:00</updated><title type='text'>Orientació a objectes al projecte</title><content type='html'>Des del temps de la carrera, la tasca que m'agradava i en la que me trobava més còmode era amb l'anàlisi. En aquell temps, que un ja té certa edat, l'anàlisi d'un sistema era, bàsicament, una anàlisi de dades. Sí, hi havia DFDs i coses semblants però sempre et feia la sensació que era un anàlisi de segona.&lt;br /&gt;&lt;br /&gt;Anàlisi era pràcticament anàlisi de dades. Anàlisi i disseny era gairebé anàlisi. El disseny de les dades era un subartefacte que no aportava gaire a l'anàlisi, i el disseny de processos, si no recordo malament, eren DFDs més detallats i el disseny de la interfície d'usuari, molta feina però hem deixava el cos de que era una cosa que no encaixava gaire amb tot l'altre.&lt;br /&gt;&lt;br /&gt;Tot allò, al meu entendre, quedava amb una anàlisi de dades i poc més. Hem trobava còmode en aquell nivell d'abstracció. M'agradava centrarme en assolir els màxims nivells de completesa, coherència i elegància en el model. Elegància era aconseguir un model en aparença senzill, però profund en quan a les seves possibilitats.&lt;br /&gt;&lt;br /&gt;Moltes coses han canviat d'aquells dies fins ara. Probablement el que arriba més sa és l'anàlisi de dades. Aquest no ha canviat gaire. La tasca d'implementar el model de dades (ara probablement compartint lloc amb un model UML de classes), és una de les parts més tranquiles del desenvolupament. Ho dic per un parell de característiques que li són úniques:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;la implementació és molt propera al model resultat de l'anàlisi&lt;/li&gt;&lt;li&gt;és la que té un model més consensuat per tothom &lt;/li&gt;&lt;li&gt;al mateix temps, sol estar en mans d'una sola persona (facilitat de control)&lt;/li&gt;&lt;li&gt;és la part més validada del sistema (tothom l'executa contínuament)&lt;/li&gt;&lt;li&gt;els diagrames faciliten el seu anàlisi&lt;/li&gt;&lt;li&gt;és una part molt estable (al manco comparada a les altres) conforme evoluciona el projecte&lt;/li&gt;&lt;/ul&gt;La efectivitat del model relacional i la seva tradició en el desenvolupament la fan una força gravitatòria que pot distorsionar tot la resta de codi del sistema. En el seu grau màxim, tot el codi queda degenerat a simples operacions bàsiques (CRUD) i, amb aquest, també la interfície de l'usuari.&lt;br /&gt;&lt;br /&gt;La resta del sistema ha canviat molt més amb els anys. Així com el model relacional semblava capaç de gestionar estructures molt gran i complexes (escalava bé), el codi no gaudia d'aquesta qualitat. Un bot important foren els llenguatges orientats a objectes. Amb aquests, el codi esdevenia més expressiu, auto-explicatiu o evident. S'augmenta la complexitat que de forma més o manco eficient, un codi podia gestionar.&lt;br /&gt;&lt;br /&gt;Emprar l'orientació a objectes per implementar la lògica de l'aplicació se troba en certes dificultats que no té la implementació del model de dades. Per començar, parla un llenguatge diferent que el seu company de treball senior, la base de dades. Problema important i molt discutit. Eines ORM (Object Relational Mapping) a l'ajuda. Com sempre feim els programadors, posem pau entre elements malentesos amb una capa. Surt el conegut com a DAO (data accés object) o Repository.&lt;br /&gt;&lt;br /&gt;I llavors venen altres problemes. A diferència de la construcció de la base de dades, la seva construcció està més dispersa i menys negociada. Imagineu-vos com seria una base de dades on tots els desenvolupadors, amb distint grau d'experiència en models relacionals i sensibilitat d'anàlisi, puguessin anar creant totes les taules que consideressin que necessiten. El model de dades permet un nivell de "contenció" per assegurar la seva qualitat sense retrassar el desenvolupament que no és fàcil tenir a la lògica.&lt;br /&gt;&lt;br /&gt;Un altre molt important és la interfície que proporciona aquest model. És a dir, com veuen altres parts del sistema la implementació del model (el conjunt de classes)?.  Alguns, com és el codi de les proves unitàries estan feliços amb el model (llevat dels casos on s'han de crear molts objectes per les proves, però això és una altre història). D'altres, com pot ser el codi web, no n'estan tant. De que se queixa aquest? Bé, té un parell de motius bons.&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Segons el model OO hi hauria d'haver tot una sèrie d'objectes "purulant" en memòria per fer la seva feina, però un servlet normalment es troba amb uns identificadors resultat del submit d'un formulari, les dades a les taules i cap objecte creat a memòria.&lt;/li&gt;&lt;li&gt;Encara que el model OO sigui una bona abstracció, encara és massa baix nivell per el codi web. Aquest vol accedir a una serie de serveis, equivalents a les accions amb sentit per l'usuari, no al model que l'habilita.&lt;/li&gt;&lt;li&gt;Hi llavors hi ha les transaccions. Les operacions s'han d'organitzar en transaccions i no sembla que el codi web sigui l'actor més adient per aquesta interpretació.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Benvinguda capa de servei. Aquesta nova capa exposa la funcionalitat del sistema a un nivell adient per el client. Aquesta capa, ben definida, respon bé a la pregunta "que pot fer el sistema?" en termes propis del domini. És, en certa manera un tercer model ( o pot ser millor, un tercer element del model) juntament amb el model relacional i l'orientat a objectes.&lt;br /&gt;&lt;br /&gt;Crec, és una idea intuïtiva, que les diferències entre la capa de servei i la capa DAO són bon indicador de la salut de la part lògica. M'explico amb un exemple.&lt;br /&gt;&lt;br /&gt;Estem implementant una tenda virtual i hem de mostrar el nombre màxim de articles (d'un determinat model) que pot adquirir un client. En el nostre cas, aquest nombre serà senzillament el nombre d'articles que tenim al magatzem d'aquest mateix model. Suposem que a la base de dades tenim els articles guardats com a instàncies individuals (no una taula model amb un camp nombre d'articles). Una forma d'implementar això seria carregar tots els objectes involucrats a memòria (articles i model) i executar un mètode de model que contés el nombre d'articles disponibles. Això sembla molt poc eficient.&lt;br /&gt;&lt;br /&gt;En aquest cas és millor emprar la funcionalitat de la base de dades (select count ...) per implementar-ho. Per tant, afegiríem un mètode al DAO per fer aquest càlcul. Com ens queda el tema?&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;// al servei&lt;br /&gt;public int nombreMaximArticlesPerCompra(long codiModel); &lt;br /&gt;&lt;br /&gt;// al dao&lt;br /&gt;public int nombreMaximArticlesPerCompra(long codiModel);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Hi ha un problema. El veieu? El dao està a un nivell de responsabilitat que no li toca. El dao fa accés a dades, no implementació de regles de negoci. A vegades (moltes) veiem el cas invers:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;// al servei&lt;br /&gt;public int nombreArticlesPerModel(long codiModel); &lt;br /&gt;&lt;br /&gt;// al dao&lt;br /&gt;public int nombreArticlesPerModel(long codiModel);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;En aquest cas, la lògica (que el màxim d'articles que se poden comprar és el nombre d'articles al magatzem) està a la part web, ja que aquesta ha de decidir invocar aquest mètode per mostrar el màxim d'articles a comprar. Si volem que la lògica estigui a on ha d'estar hem de fer que cada capa exposi la funcionalitat de forma consistent amb les seves responsabilitats:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;// al servei&lt;br /&gt;public int nombreMaximArticlesPerCompra(long codiModel); &lt;br /&gt;&lt;br /&gt;// al dao&lt;br /&gt;public int nombreArticlesPerModel(long codiModel);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Aquest exemple anterior mostra a més un altre cas molt freqüent. Fitxeu vos que tota la lògica queda implementada en operacions a la base de dades. Aquí sembla que era l'opció més raonable (si ningú me corregeix), però en moltes aplicacions, degut a la tradició estructurada i el comford amb l'SQL de molts programadors, és tota la lògica la que acaba en l'SQL. Les nostres classes esdevenen simples contenidores de dades i sentim un senyor barbut darrera nostra que ens diu "anemic objects! anemic objects!" (&lt;a href="http://www.flickr.com/photos/pragdave/173640462/"&gt;aquest&lt;/a&gt;). És un risc i és real.&lt;br /&gt;&lt;br /&gt;Se pot implementar així, però llavors no feim un ús correcte de les tecnologies. En aquest cas una eina ORM probablement molesti més que ajudi. Millor JDBC, data objects i tira milles.&lt;br /&gt;&lt;br /&gt;Advantatges que justifiquin la complexitat afegida per introduir un tipus com Hibernate al nostre grup només s'obtenen quan ens comprometem amb el model orientat a objectes plenament. És a dir, otorgem a les nostres classes el major grau de responsabilitat en la implementació de les regles de negoci. És un tema de coherència: si vols anar com els joves, amb calçons d'aquests que et cauen als genolls, la roba interior ha d'estar a l'altura. Altrament els resultats són catastròfics.&lt;br /&gt;&lt;br /&gt;M'agradaria parlar de una altre interfície, la REST, però això ja és massa llarg.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-8070749349994836806?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/8070749349994836806/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=8070749349994836806' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/8070749349994836806'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/8070749349994836806'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2008/07/orientaci-objectes-al-projecte.html' title='Orientació a objectes al projecte'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-4274440542972598996</id><published>2008-07-12T18:56:00.000+02:00</published><updated>2008-07-12T19:20:46.728+02:00</updated><title type='text'>desenvolupadors i administradors</title><content type='html'>Per les característiques de l'organització per la que treballo, els equips d'explotació són gestionats per gent externa al projecte. És a dir, l'equip de desenvolupadors està aïllat de l'equip de gent al càrrec del l'administració dels sistemes en producció. A més, les polítiques de seguretat són bastant severes i tenim poc accés als servidors.&lt;br /&gt;&lt;br /&gt;Suposo que aquesta estructura és necessària per les peculiaritats de l'organització, com passa a molts altres llocs.&lt;br /&gt;&lt;br /&gt;A part d'arguments relatius a l'organització, aquesta postura troba una justificació en la diferència en el cos de coneixements, les eines emprades i les tasques que fan els administradors respecte als dels desenvolupadors.&lt;br /&gt;&lt;br /&gt;Però el fet que hi hagi arguments que ho justifiquin no implica que sigui sempre la millor opció. Els problemes derivats d'aquesta separació són grans:&lt;br /&gt;&lt;br /&gt;Un primer grup obvi de deficiències és no poder accedir a informació del sistema com ús de CPU, memòria, xarxa, etc per veure com se comporta l'aplicació en distints moments de càrrega.&lt;br /&gt;&lt;br /&gt;En alguns casos, no saber quin és l'ús d'espai de disc o de base de dades, pot arribar a aturar el servei. Ens ha passat, per exemple, per quedar sense espai al tablespace. Conèixer deficiències de l'aplicació en aquests sentits quan abans millor és crític.&lt;br /&gt;&lt;br /&gt;Qualsevol operació en l'entorn de producció, per exemple desplegar una versió que corregeix un bug, requereix d'en un procés lent de preparar sol·licitut / enviament de mail signat / recepció per part de l'equip de producció.&lt;br /&gt;&lt;br /&gt;De totes, probablement la que més me molesta és no poder accedir al coneixement de l'administrador de sistemes. Ens aniria molt bé la seva ajuda per muntar un sistema semblant al de producció a escala per poder fer les proves d'acceptació i rendiment en els nostres locals. Hi ha entorn de preproducció però no és semblant al de producció en alguns aspectes importants i tampoc a aquell podem accedir-hi directament.&lt;br /&gt;&lt;br /&gt;També aquesta falta de contacte més directe fa que sortir de les opcions "estàndards" de configuració dels sistemes d'explotació sigui molt complicat. Una "tontaria" com fer que el contingut estàtic sigui servit directament per un apache, sense passar per el servidor d'aplicacions, és una negociació esgotadora. No parlem ja de poder escollir quines versions de cada producte que forma l'arquitectura de desplegament.&lt;br /&gt;&lt;br /&gt;No només els desenvolupadors som els perjudicats. Les aplicacions poden tenir característiques que facin la vida més fàcil als administradors. Poder deshabilitar temporalment alguns mòduls, poder avisar als usuaris d'una inminent aturada o poder accedir a dades de l'aplicació sense passar per els threads de servei http (per exemple, si queden per alguna errada queden tots bloquejats) no són molt complexes de implementar. Una comunicació directe administrador - desenvolupador pot facilitar la vida a tots dos.&lt;br /&gt;&lt;br /&gt;Un consultor molt conegut, tenia com a pràctica habitual fer que personal d'un departament fos l'encarragat de presentar-lo i introduir-lo als altres departaments (per exemple, un membre de l'equip de desenvolupament l'havia de presentar als membres de l'equip de sistemes). Deia que molts clients afirmaven que aquesta breu "incursió" en altres territoris de la mateixa empresa havia estat tan profitos que per si sola ja justificava la consultoria.&lt;br /&gt;&lt;br /&gt;Aquesta qüestió me recorda a una semblant que és la separació entre els usuaris i els desenvolupadors. La literatura tradicional ha construït un cos de coneixements considerable respecte a com formalitzar aquesta separació i, al mateix temps, afrontar la necessitat que tenim de saber que és el que fan i necessiten els usuaris. Estic parlant de tot el que engloba el terme anàlisi dels requeriments.&lt;br /&gt;&lt;br /&gt;A la pràctica, la separació usuaris - desenvolupadors ha estat posada en dubte per molta gent. La separació suposa una "des-naturalització" del producte resultant que fa que no s'ajusti al que volen els usuaris. L'aplicació parla l'idioma dels desenvolupadors, que no és el mateix que el dels usuaris.&lt;br /&gt;&lt;br /&gt;No crec que tot el dit aquí sigui diferent a l'anterior. Usuaris, desenvolupadors i administradors són tres "usuaris" del sistema, que té l'aplicació com a element central. Quan desenvolupem, el nostre codi no és encara viu. Només el veiem respirar quan observem com l'empren els usuaris, fins quin punt l'ajuden en les seves tasques, i com se comporta amb els recursos físics que té. Deia Michael Nygard a "Release It" que el desenvolupament no acaba un cop lliurat el sistema sinó que aquest és el moment en que comença. Tot el que ha passat abans només és preparació.&lt;br /&gt;&lt;br /&gt;Evidentment, no podem tenir els tres grups en una mateixa sala, parlant tots en tots, però sí que s'ha de conèixer el preu que té el grau de separació que imposem entre tots ells. Llavors la qüestió esdevé la clàssica balança que hem de equilibrar.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-4274440542972598996?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/4274440542972598996/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=4274440542972598996' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/4274440542972598996'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/4274440542972598996'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2008/07/desenvolupadors-i-administradors.html' title='desenvolupadors i administradors'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-544114521581214037</id><published>2008-07-05T17:22:00.000+02:00</published><updated>2008-07-05T17:25:39.510+02:00</updated><title type='text'>Polèmiques</title><content type='html'>En aquests temps amb tantes discussions :&lt;br /&gt;&lt;ul&gt;&lt;li&gt;codi lliure vs. codi privatiu&lt;/li&gt;&lt;li&gt;llenguatges compilats vs. llenguatges dinàmics&lt;/li&gt;&lt;li&gt;linux vs. microsoft&lt;/li&gt;&lt;li&gt;etc etc&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;només recordar que s'ha de respectar tot.&lt;br /&gt;Com amb els editors:&lt;br /&gt;&lt;br /&gt;Hi ha programadors amb Idea i n'hi ha sense idea, però tots igualment respetables ;-) *&lt;br /&gt;&lt;br /&gt;* val, ho reconec, la frase no és original meva.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-544114521581214037?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/544114521581214037/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=544114521581214037' title='3 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/544114521581214037'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/544114521581214037'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2008/07/polmiques.html' title='Polèmiques'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-8640797330627536633</id><published>2008-07-02T21:40:00.000+02:00</published><updated>2008-07-02T22:18:04.879+02:00</updated><title type='text'>informàtica ficció</title><content type='html'>Quan fem un bocí de codi, amb una funcionalitat més o manco completa, la podem provar per veure si funciona. Ara bé, funcionar pot significar moltes coses. Una d'elles és que la funcionalitat se implementi correctament, una altra és que sigui mantenible, eficient, escalable, etc, etc. Diguem-li atributs o qualitats.&lt;br /&gt;&lt;br /&gt;Si fem una prova senzilla, una execució o una ullada superficial al codi, podem fer-nos una idea aproximada per cada un d'aquests atributs (correctesa, temps de resposta, mantenibilitat, etc ).&lt;br /&gt;&lt;br /&gt;Seguint amb aquests atributs, per alguns d'ells és possible definir una espècie de bateria més formal per avaluar-la. Exemples : una prova de estres per avaluar l'eficiència, un joc de proves més o manco complet per la correctesa, etc.&lt;br /&gt;&lt;br /&gt;Podríem dir que cada qualitat pot tenir una sèrie de dimensions en les que s'obté una mesura&lt;br /&gt;&lt;ul&gt;&lt;li&gt;L'avaluació del rendiment tendria com a dimensions el nombre de peticions totals, el nombre de peticions concurrents, el volum de informació requerit o retornat etc.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Per els jocs de proves, si són unitaris, sembla definit per els seu conjunt de paràmetres, sempre que sigui una funcionalitat sense efectes colaterals.&lt;/li&gt;&lt;li&gt;La mantenibilitat sembla només tenir mesures indirectes (cas típic, complexitat assignat segons una determinada mètrica)&lt;/li&gt;&lt;/ul&gt;Els tres exemples anteriors són clarament distints.&lt;br /&gt;&lt;br /&gt;En un, el primer (proves de rendiment o estres), les dimensions eren lineals. Si no me és suficient provar 100 invocacions amb 5 threads concurrents, ho provo amb 300 amb 10 threads concurrents.&lt;br /&gt;&lt;br /&gt;En un altre, el segon (proves de correctesa), cada prova nova requereix de descobrir valors útils per cada dimensió (paràmetre en aquest cas). Hi ha heurístiques, però només són això.&lt;br /&gt;&lt;br /&gt;En el tercer (mantenibilitat), no hi ha ni tan sols dimensions. Podríem dir que és un únic punt. Això ens deixa en una mala situació per anticipar-nos a com la funcionalitat respondrà a futurs canvis. Com la vida mateixa, només el pas del temps ens dona una idea del seu valor real. Però llavors ja poc podem fer.&lt;br /&gt;&lt;br /&gt;Com seria un prova d'estres de mantenibilitat? ni idea, però hauria de tenir un aspecte així:&lt;br /&gt;&lt;br /&gt;Tenim una porteta per introduir el codi a avaluar i, a part, una serie de palanques. Suposem que ajustem una palanca a 4 requeriments nous per cada 100 requeriments i any (volatilitat dels requeriments, per dir-ho així), i l'altre li deim que ens interessa la feina a fer en 5 anys. El resultat ens diu que ens durà 10 homes / dia per any de cost de manteniment.&lt;br /&gt;&lt;br /&gt;Llavors, igual que amb les proves d'estres, podríem avaluar la conveniència o no de refactoritzar el nostre codi.&lt;br /&gt;&lt;br /&gt;Seria xulo no?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-8640797330627536633?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/8640797330627536633/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=8640797330627536633' title='2 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/8640797330627536633'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/8640797330627536633'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2008/07/informtica-ficci.html' title='informàtica ficció'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-1003559601064596370</id><published>2008-06-20T09:11:00.000+02:00</published><updated>2008-06-20T09:17:05.976+02:00</updated><title type='text'>Comentaris al Data Quality</title><content type='html'>Fa unes setmanes vaig fer una comanda de 5 llibres, un dels quals ja vaig començar a &lt;a href="http://domingosebastian.blogspot.com/2008/05/data-quality-el-llibre.html"&gt;comentar&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Darrerament el tren ha fet una bona feina en la noble tasca de fomentar la lectura (assumeixo que tots el lectors són d'àmbit local), així que ja estan 4 dels 5 llegits ( Continuous Integration, Data Quality Assesment, Release It! i Scrum and XP from the Trenches ). L'altre Visualitzing Data està pendent de ser començat. &lt;br /&gt;&lt;br /&gt;Volia acabar de donar la meva opinió del Data Quality Assessment. Els motius són per acabar de "pair" una mica el material, donar-vos una idea aproximada del que ensenya i, per que no, pot ser vos ajudi a decidir si comprar-ho o no.&lt;br /&gt;&lt;br /&gt;Les dues primeres parts del llibre (un 60% del seu tamany), proporciona idees i estructura tipus de regles per identificar errades en les nostres dades. Ja comentava un dels casos més senzills (falsos nulls). També podem cercar "forats" temporals en registres històrics, inconsistències entre esdeveniments i estats (p.e. un registre ens diu que un treballador ha sortit de l'empresa, però el seu estat és actiu i no hi ha cap registre a l'historial amb la "re-contractació) etc, etc.&lt;br /&gt;&lt;br /&gt;Aquesta part del llibre, tot i ser interessant, encaixa molt amb aspectes que ja hagem pogut pensar tots els que hem treballat amb base de dades. Afegiré, com aspecte negatiu, que fa massa referències a problemes d'integritat referencial, quan la majoria de lectors estic segur que empren base de dades on això ja està controlat.&lt;br /&gt;&lt;br /&gt;Fins aquí, com dic, el llibre aporta bones idees però diguem que encara no aconsegueix "revolucionar" la teva visió del tema. I quina era la meva visió? idò això, un conjunt de regles, expressades en forma de selects o procediments senzills que et donaven tot un conjunt de registres "sospitosos" o clarament erronis.&lt;br /&gt;&lt;br /&gt;Què fer a partir d'aquí? be, alguns pot ser els puguis arreglar tu, alguns pot ser puguis trobar la font d'informació bona, d'altres, segurament, no hi pots fer res. Però, fet això ... res més.&lt;br /&gt;&lt;br /&gt;Aquesta situació la podríem resumir amb que la teva resposta a un "jefe" que demana "necessit un informe basat en les teves dades. Que tal estan?", només podia ser un "ppfffff.... bueno ... bastant bé... hi ha algunes errades, però ....."&lt;br /&gt;&lt;br /&gt;Bona resposta no? enginyeria en estat pur ! ;-)&lt;br /&gt;&lt;br /&gt;Au idò, cap a la tercera part del llibre!&lt;br /&gt;&lt;br /&gt;Aquí és a on s'ofereix un mètode per estructurar tots els resultats individuals del procés anterior i oferir una informació el més útil possible. Es organitzar les metadades (com ens agrada aquest terme...) per catalogar tota la informació.&lt;br /&gt;&lt;br /&gt;Molt resumit:&lt;br /&gt;&lt;br /&gt;Tenim les dades del propi esquema de base de dades: taules i columnes.&lt;br /&gt;Tenim el conjunt de regles, relacionades amb les taules i columnes que valida.&lt;br /&gt;I tenim els registres individuals (les nostres dades de producció), cada una amb una certa probabilitat d'error segons li assignen les regles que estan relacionades amb la seva taula.&lt;br /&gt;&lt;br /&gt;Tot això, després d'un procés de refinament molt important (i costos), permet tenir una idea molt més clara del que és la qualitat de les teves dades.&lt;br /&gt;&lt;br /&gt;Anem a la pregunta del jefe:&lt;br /&gt;pregunta : "necessit un informe basat en les teves dades. Que tal estan?"&lt;br /&gt;resposta : "quines dades necessites per aquest informe?"&lt;br /&gt;p. "és per obtenir el nombre de professors que l'any passat feien menys de 15 hores i enguant més de 15"&lt;br /&gt;r. "per quin tipus de centre?"&lt;br /&gt;p. "per tots"&lt;br /&gt;&lt;br /&gt;(després de fer les consultes al catàleg)&lt;br /&gt;r. "l'informe estarà basat amb unes dades que estan al 90% completes i amb una correctesa del 87%"&lt;br /&gt;p. "i si només son els públics"&lt;br /&gt;r. "llavors la completesa és del 94% i la correctesa del 92%"&lt;br /&gt;&lt;br /&gt;millor no? ;-)&lt;br /&gt;&lt;br /&gt;Llavors com sempre en la informàtica, les "fotos" amb l'estat valen per molt poc. Ràpidament caduquen. La gràcia de tota aquesta informació és que es pot anar actualitzant i ens ofereix una monitorització de l'estat de salut de la nostra base de dades. Ens pot ajudar a detectar processos de migració o usuaris que fan baixar la qualitat de les nostres dades i podem actuar en conseqüència (sempre dins dels límits legals... clar).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-1003559601064596370?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/1003559601064596370/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=1003559601064596370' title='1 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/1003559601064596370'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/1003559601064596370'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2008/06/comentaris-al-data-quality.html' title='Comentaris al Data Quality'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-6626031784550586597</id><published>2008-06-01T20:57:00.000+02:00</published><updated>2008-06-01T21:26:51.123+02:00</updated><title type='text'>Decàleg per un programador novell</title><content type='html'>&lt;span style="font-weight: bold;"&gt;1.- Fes canvis petits&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;/span&gt;Quan programis intenta tenir sempre una versió funcional.  Programa els teus canvis en passos petits i controlats.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;2.- No facis copy paste sense saber que fa el codi&lt;br /&gt;&lt;/span&gt;Coneix com funciona cada línia del teu codi. Intenta evitar els copy - paste, però si els fas, que sigui de codi que saps explicar.&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;br /&gt;3.- Vigila els warnings del editor&lt;br /&gt;&lt;/span&gt;Els warnings dels editors no poden ser ignorats. Entén quin és el problema. Corregil. Si hi ha algun warning que no s'aplica al teu codi, configura el teu editor per que no el mostri. Manté el codi net de warnings.&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;4.- Familiaritzat amb totes les eines (sobre tot  l'editor)&lt;br /&gt;&lt;/span&gt;No te conformis amb les funcionalitats mínimes de les eines. Quan demanes ajuda a un senior, intenta entendre tot el que fa. Marcat com a objectiu no haver de necessitar més la seva ajuda per un problema semblant.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;5. -Demana que un senior et corregeixi el codi&lt;br /&gt;&lt;/span&gt;Un cop tens una funcionalitat completa, revísala. Deixa-la el millor que puguis. Després, i només després d'això, demana-li a un senior que t'ho revisi i te expliqui que és millorable i perquè. Ves a berenar amb ell. Paga-li el cafè. Demana-li com va aprendre el llenguatge, les seves primeres errades, etc.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;6.- Mira codi escrit per els senior&lt;br /&gt;&lt;/span&gt;Revisa codi escrit per el senior. No ho facis per alguna funcionalitat que has de implementar o, al manco, no abans d'haver-ho intentat. Revisa com ha resolt codi que implementa una funcionalitat semblant a la que tu has implementada.&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;7.- Ajuda als altres novells&lt;br /&gt;&lt;/span&gt;Oferit per ajudar als altres novells. Tot d'una que hagis après alguna cosa, explica-la als altres.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;8.- Refés sense pietat el codi escrit&lt;br /&gt;&lt;/span&gt;El primer codi sempre serà molt defectuós. Fes el possible, dins la realitat del lloc on fas feina, per corregir i refer tot el codi que vegis que no és correcte. No deixis racons del programa que saps que són incorrectes. Observa si el que has après realment millora el que tenies escrit.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;9.- Llegeix algun llibre introductori&lt;br /&gt;&lt;/span&gt;Compra, demana o roba un llibre del llenguatge. Mira que estigui ben recomanat. L'espai del que un bon autor disposa en un llibre no ho iguala cap post, tutorial, etc.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;10. -No critiquis&lt;br /&gt;&lt;/span&gt;La crítica és necessària en la nostra professió. Els dogmes de fe o les alineacions amb determinades postures de forma acrítica no són bones. Ara bé, quan estas aprenent no és el moment de fer-ho. Aparca l'esperit crític. Just aprèn, aprèn i aprèn. La crítica ja la faràs més endavant. A més, encara que hi hagi llenguatges més adients que d'altres, en tots ells, estic convençut, es possible escriure bons programes.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-6626031784550586597?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/6626031784550586597/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=6626031784550586597' title='1 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/6626031784550586597'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/6626031784550586597'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2008/06/decleg-per-un-programador-novell.html' title='Decàleg per un programador novell'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-6585399902837303101</id><published>2008-05-31T11:48:00.000+02:00</published><updated>2008-05-31T12:15:24.225+02:00</updated><title type='text'>Animal a = new Cat ("Missy");</title><content type='html'>&lt;p style="margin-bottom: 0cm;"&gt;Duc un parell de dies donant voltes al tema de llenguatges dinàmics vs compilats (o variacions en els noms/matisos que vulgueu).&lt;/p&gt;&lt;p style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/p&gt;  &lt;p style="margin-bottom: 0cm;"&gt;És un tema important a molts nivells. Des del punt de vista teòric permet comparar de forma molt clara els recursos de cada una de les aproximacions i fer explícites les seves suposicions de partida (els seus valors, per dir-ho així). Des del punt de vista pràctic, sembla irresponsable ignorar el que problablement sigui un dels moviments en l'àmbit de la programació més importants. Com a persona que viu dins del món de la programació, estic intrigat per veure com acaba aquest moment de crisi (en un bon sentit de la paraula, com a pertorbació d'un equilibri anterior que durà a un possiblement millor).&lt;/p&gt;&lt;p style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/p&gt;  &lt;p style="margin-bottom: 0cm;"&gt;La meva situació actual no hem permet aficar-m'hi tot el que voldria. A on faig feina, les tecnologies a emprar estan en gran mesura fora del nostre control, i les altres ocupacions professionals que tinc (formació) hem deixen poc marge de maniobre. Per tant, no puc tractar la questió com m'agradaria : aprendre python, django o similar i comparar les dues aproximacions.&lt;/p&gt;&lt;p style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/p&gt;  &lt;p style="margin-bottom: 0cm;"&gt;N'aaloy fa uns bons resums que hem permeten agafar una idea del que hi ha al costat dinàmic. He de dir però que al seu &lt;a href="http://trespams.com/2008/05/28/capa-de-negoci-django/"&gt;darrer apunt&lt;/a&gt; m'he quedat amb "ganes de més". Crec que la part gruixada de la qüestió és a :&lt;/p&gt;&lt;p style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/p&gt;  &lt;p style="margin-bottom: 0cm;"&gt;"Segons la nostra aplicació podem anar afegint custom managers que ens permetran definir regles damunt les dades que volem obtenir, mètodes de classe per definir les funcionalitats que vagin damunt tots els elements de la taula, i missatges que ens ajudin a manipular la informació i les seves relacions."&lt;/p&gt;&lt;p style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/p&gt;  &lt;p style="margin-bottom: 0cm;"&gt;Hem faltaria veure algun exemple per fer-men una idea més clara de com funcionen.&lt;/p&gt;&lt;p style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/p&gt;  &lt;p style="margin-bottom: 0cm;"&gt;En ricardo diu en &lt;a href="http://trespams.com/2008/05/28/capa-de-negoci-django/#comentari_832"&gt;un comentari&lt;/a&gt;   &lt;/p&gt; &lt;p style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style="margin-bottom: 0cm;"&gt;"El que mai faria es fer servir un llenguatge compilat i sense suport de hashes i arrays dinàmics."&lt;/p&gt;  &lt;p style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style="margin-bottom: 0cm;"&gt;Suposo que en el context de python o php deuen tenir un significat més concret del que conec. Estructures que empren funcions de hash, estructures tipus diccionari, o equivalents a arrays amb nombre d'elements indeterminats o amb elements heterogenis es poden tenir en Java. Si no és amb aquest sentit, simplement se m'escapa la questió :-(&lt;/p&gt;  &lt;p style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style="margin-bottom: 0cm;"&gt;Mon pare hem solia donar bons consells. Un d'ells era primer enten, després critica. Com ja he dit, no estic en condicions de fer una comparació de costat a costat. De fet, tampoc vull criticar !&lt;/p&gt;  &lt;p style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style="margin-bottom: 0cm;"&gt;Sense temps (per ara) d'aprendre python, com puc anar preparant-me per quan arrivi el moment? Pensat damunt del tema, notava que prescindir de determinats aspectes dels llenguatges compilats (en particular del Java) hem semblava “intuitivament” incòmode. Així que vaig decidir intentar concretar el bagatge que tenim, amb els advantatges que suposen. És a dir, convertir la idea intuitiva en explícita.&lt;/p&gt;  &lt;p style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style="margin-bottom: 0cm;"&gt;Per fitxar el context, hem refereixo a sistemes complexes, en els sentit de sistemes amb moltes funcionalitats, regles de negoci no trivials (es a dir, els CRUD son una part mínima), i, important, desenvolupats per més de, posem, 5 persones i que s'han de mantenir durant més de 4 anys. Amb els afegits i modificacions que hi solen haver.&lt;/p&gt;  &lt;p style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style="margin-bottom: 0cm;"&gt;Per que és important això? ho comento amb un exemple. Ens varem trobar un requeriment que era llistar el possibles noms de grups (una lletra amb majúscula) amb unes certes condicions. Un company ho va conseguir implementar amb una sola select. Era molt eficient, compacte i podia provar-se fàcilment (no s'havia d'arrancar el programa sinó que bastava una cònsola SQL). És a dir, obtenia un 10 en puntació en termes de rendiment, productivitat (linies de codi emprades) i temps per provar-ho.&lt;br /&gt;&lt;/p&gt;&lt;p style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style="margin-bottom: 0cm;"&gt;Quan me la va mostrar (la consulta), la vaig mirar un parell de minuts i no vaig ser capaç de dir el que treia ! (tb la consulta)  &lt;/p&gt;  &lt;p style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style="margin-bottom: 0cm;"&gt;Quin era el problema? La solució era molt enginyosa, demostrava imaginació per part del programador, però no era explícita. La llegies i no et deia que estava fent. Això pot ser poc important per alguns sistemes però no és acceptable per la situació que tracto.&lt;/p&gt;  &lt;p style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style="margin-bottom: 0cm;"&gt;Que fa que hem trobi còmode programant en Java aquest tipus d'aplicacions? Un recurs obvi per reduir la complexitat és la creació de classes. Les classes (juntament amb l'herència) permeten encapsular parts del sistema fent que la resta pugui ignorar els detalls encapsulats i treballar just els aspectes que li son propis. Evidentment, classes i herències no són pròpies dels llenguatges compilats, sinó dels OO, els quals tant poden ser estàtics com dinàmics, així que cal afinar més.&lt;/p&gt;  &lt;p style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style="margin-bottom: 0cm;"&gt;Vegem un &lt;a href="http://en.wikipedia.org/wiki/Polymorphism_in_object-oriented_programming#Python"&gt;bocí de codi&lt;/a&gt; tret de la wikipedia:&lt;/p&gt;   &lt;pre&gt;&lt;br /&gt;class Animal:&lt;br /&gt;&lt;br /&gt;def __init__(self, name):    # Constructor of the class&lt;br /&gt;      self.name = name&lt;br /&gt;&lt;br /&gt;class Cat(Animal):&lt;br /&gt;def talk(self):&lt;br /&gt;     return 'Meow!'&lt;br /&gt;&lt;br /&gt;class Dog(Animal):&lt;br /&gt;def talk(self):&lt;br /&gt;     return 'Woof! Woof!'&lt;br /&gt;&lt;br /&gt;a = Cat('Missy')&lt;br /&gt;b = Cat('Mr. Bojangles')&lt;br /&gt;c = Dog('Lassie')&lt;br /&gt;&lt;br /&gt;for animal in (a, b, c):&lt;br /&gt;   print animal.name + ': ' + animal.talk()&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p style="margin-bottom: 0cm;"&gt;No empro codi de python per criticar-lo. Simplement per centrarme en el que Java té de diferent. Pot ser el que python no té,  és perque no li és necessari. S'ha d'evitar tenir solucions cercant problemes!&lt;/p&gt;  &lt;p style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style="margin-bottom: 0cm;"&gt;L'exemple anterior és un cas de polimorfisme. animal.talk invoca distintes implementacions en funció del tipus del objecte que s'executa.  &lt;/p&gt;  &lt;p style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style="margin-bottom: 0cm;"&gt;En Java (a part de tenir el doble de codi per fer el mateix) la part diferent que me interessa seria:&lt;/p&gt;  &lt;p style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style="margin-bottom: 0cm;"&gt;Animal a = new Cat ("Missy");&lt;/p&gt;  &lt;p style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style="margin-bottom: 0cm;"&gt;Que ens dona el fet de tenir declarada la variable a com Animal?  &lt;/p&gt;  &lt;p style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style="margin-bottom: 0cm;"&gt;La primera, obvia, és la comprovació del compilador. No li donarem més voltes ja que sembla ser la part principal i més tractada del debat que tenim entra mans.&lt;/p&gt;  &lt;p style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style="margin-bottom: 0cm;"&gt;El que me interessa és que fa explícit que, per aquesta part de codi, estem tractant amb animals. Aquesta part de la lògica és independent del tipus concret d'animal. Estem fent ús de l'abstracció animal (no de la abstracció que tenen els animals, sinó del concepte abstracte creat, animal (no, no és que et digui animal, és el nom del concepte, ejem... aturo) ) no només per evitar duplicar cert codi, sino per fer tot el problema més manejable.  &lt;/p&gt;  &lt;p style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style="margin-bottom: 0cm;"&gt;Aquest ús del polimorfisme, juntament amb l'ús de les interficies amb la mateixa idea ( p.e. List elements = new ArrayList() ), és una de les principals eines que tenim per gestionar la complexitat. De fet, sovint, és una de les grans motivacions per cercar conceptes abstractes que permetin expresar de manera molt més senzilla les regles més complexes del programa.&lt;/p&gt;  &lt;p style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style="margin-bottom: 0cm;"&gt;L'abstracció, com a recurs necessari per gestionar la complexitat, implica en un moment determinat, permeter-nos ignorar determinats detalls que no són relevants al tema que tractem. Aquesta abstracció és de definició negativa: ens diu que és el que &lt;span style="font-weight: bold;"&gt;no&lt;/span&gt; podem fer. Amb na Missy (definida com a variable de tipus Animal), no podem emprar mètodes que són particulars de Cat. Abstracció, en aquest sentit, és contrari a flexibilitat, argument fort dels llenguatges dinàmics. Serà una cosa que anyoraré en programar en llenguatges dinàmics (si ho arrivo a fer) o no?&lt;/p&gt;&lt;p style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style="margin-bottom: 0cm;"&gt;M'he quedat curt amb els temes que volia tractar però això ja se m'ha fet massa llarg. Continuarà.&lt;br /&gt;&lt;/p&gt;&lt;p style="margin-bottom: 0cm;"&gt;&lt;/p&gt;&lt;p style="margin-bottom: 0cm;"&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-6585399902837303101?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/6585399902837303101/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=6585399902837303101' title='1 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/6585399902837303101'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/6585399902837303101'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2008/05/animal-new-cat-missy.html' title='Animal a = new Cat (&quot;Missy&quot;);'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-2836920930468357594</id><published>2008-05-25T17:18:00.000+02:00</published><updated>2008-05-25T17:25:06.688+02:00</updated><title type='text'>El tipat</title><content type='html'>Hem té força interessat la gran aportació que poden fer els llenguatges dinàmics a la programació en general.&lt;br /&gt;&lt;br /&gt;Llegia l'&lt;a href="http://gallir.wordpress.com/2008/05/24/lenguajes-dinamicos-programadores-y-fud/"&gt;entrada d'en gallir&lt;/a&gt;  i tots els comentaris allà escrits i m'han vingut ganes de fer-hi alguna aportació, però m'han fugit aviat. No crec que es pugui realitzar un debat interessant amb tant de renou en forma de comentaris despectius, paternalistes, insultants i injustificats. No va per tots, es clar, per exemple aaloy es una excepció i desenvolupa de forma molt interessant el seu comentari en una &lt;a href="http://trespams.com/2008/05/25/llenguatges-dinamics/"&gt;entrada pròpia&lt;/a&gt; .&lt;br /&gt;&lt;br /&gt;En lloc de comentar l'entrada allà vaig a alimentar una mica aquest bloc que està un poc abandonat.&lt;br /&gt;&lt;br /&gt;Com deia, aaloy detalla alguns dels punts on podem comparar de manera bastant objectiva les dues aproximacions (compilada vs. interpretada).&lt;br /&gt;&lt;br /&gt;Veig sentit a tot el que diu (no puc dir més ja que hem falten coneixements amb phyton o equivalents), sobre tot a les diferències en la durada dels cicles de desenvolupament. No és que necessitis un parell de segons més per poder provar l'efecte d'un petit canvi en el codi Java. És sobretot, l'efecte d'aquests segons en la teva concentració quan programes.&lt;br /&gt;&lt;br /&gt;Trobo que falta justificar algunes frases com "El nombre de línies de codi que escriu un programador al dia és una constant.". Hem sona força contra-intuïtiva. O "...y ser el número de errores directamente proporcional al número de líneas que se escriben ..". No entro a dir si són certes o falses, però hem falta una justificació.&lt;br /&gt;&lt;br /&gt;Però, sintetitzant, articula molt bé una serie punts sobre els que es pot establir una comparació justa entre ambdós entorns.&lt;br /&gt;&lt;br /&gt;Deia al començament que aquesta polèmica hem té interessat, ja que trobo que pot millorar la qualitat de les nostres eines quan programem. Actualment me és impossible entrar a estudiar un entorn com python, ja que les meves ocupacions m'absorbeixen en un 110%. Però estic molt interessat en fer-ho i la meva actitud envers ell no és, ni molt manco, negativa.&lt;br /&gt;&lt;br /&gt;Tradicionalment m'he dedicat més a desenvolupar la part de la lògica de negoci i, només marginalment, he treballat en la part de presentació. Crec que la part de presentació és la que més fàcilment pot treure profit dels entorns interpretats. En Java, l'àmbit que conec, les tecnologies per la creació de la capa web difícilment justifiquen la seva relació funcionalitat aportada / cost de desenvolupament. Ho feien fa un temps, quan la part de lògica tenia els infumables EJB CMP i germans ... . Era com en Saviola, que semblava bo devora els Retzinguer i companyia.&lt;br /&gt;&lt;br /&gt;Programar la part de lògica és més complexe. Desenvolupar el codi de la lògica de negoci ho veig més com una tasca de modelat que com de implementació. Sovint la gènesis del programa és un anàlisi ER més o manco pensat, i després a produir CRUD com a xurros i un parell de llistats més o manco complexes. Totes les eines RAD que he vist, els 4GL d'un temps, o la demo que vaig seguir del Django mostren com poden fer això rapidissimament.&lt;br /&gt;&lt;br /&gt;D'acord, molt bé. És una part, però no és el que cerco per fer millor la meva feina. Aproximacions com l'anterior fan que capa de negoci quedi com un simple SQL  que empra classes en lloc de taules. El model de dades mostra cert coneixement de les dades emprades i tota l'aplicació es queda aquí.&lt;br /&gt;&lt;br /&gt;Aquestes aplicacions son poc usables. Entenen les dades que hi ha, però no els processos. No s'adapten al que l'usuari té en el cap sinó que demanen que sigui l'usuari que converteixi tot el que coneix i necessita del sistema en una serie d'altes / baixes / modificacions d'elements bàsics. A més, la naturalesa sembla resistir-se a aquestes simplificacions. Els usuaris necessiten de certes regles complexes que el desenvolupament anterior no ha estudiat en detall en cap moment. No hem val l'anàlisi de dades. Allà ens demanem si les taules definides tenen tota la informació suficient per la tasca, però no es fa cap esforç de modelar aquesta regla. Aquestes regles apareixen quan el programa ja està en producció i són els canvis de requeriments que tant mal fan i dels que solem culpar als usuaris.&lt;br /&gt;&lt;br /&gt;El camí viciat anterior és romp si feiem del desenvolupament de la lògica de negoci una tasca amb més responsabilitats. El codi d'aquesta capa no és una implementació d'una llista de requeriments. És un modelat que té en compte més coses de les que pot tractar la base de dades. S'ha d'anar fent en paralel. Es orgànic. Hi ha molt de fer i refer.  Duu a crear una taula, per després separar-la i, uns dies després tornar a ajuntar-la.&lt;br /&gt;&lt;br /&gt;El seu resultat és un conjunt de classes i interfícies ( i sí, han de ser POJOS, o al manco és el que penso avui) que creen en el nostre llenguatge el &lt;span style="font-weight: bold;"&gt;vocabulari&lt;/span&gt; de l'usuari. Aquest vocabulari inclou conceptes associats a informació tangible ( Alumne, Centre ...) i a processos ( CalculMitjana, Promocio, etc ). La seva evolució va des de els primer intents més o manco ingenus i intuïtius (p.e. Habitació ) cap a altres més abstractes (UnitatAllotjament) que són els que, curiosament, empren els nostres usuaris. Aquest camí implica esborrar sense pietat moltes coses fetes. La prova de foc, és aconseguir que les conversacions entre dos usuaris experts en l'àmbit que hem de implementar és dugui a terme just amb els conceptes que són explícits en el nostre codi.&lt;br /&gt;&lt;br /&gt;Dit això, torno al tema que vull tractar a aquesta entrada.&lt;br /&gt;&lt;br /&gt;Vull tenir molt clar, i ara no ho tenc, el que significa prescindir d'expresar en el codi tots els aspectes de tipat. No per por a cometre un error de sintaxis. Els tipus així desenvolupats no son un recurs de captura d'errors de compilació sinó la part fonamental del meu programa. No l'objectiu final, que és fer una aplicació que faciliti la vida als meus usuaris, però sí la meva eina principal per aconseguir-ho.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-2836920930468357594?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/2836920930468357594/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=2836920930468357594' title='1 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/2836920930468357594'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/2836920930468357594'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2008/05/el-tipat.html' title='El tipat'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-3709190487871197031</id><published>2008-05-09T20:04:00.000+02:00</published><updated>2008-05-09T20:10:10.986+02:00</updated><title type='text'>Data Quality (el llibre)</title><content type='html'>Just he començat a llegir el llibre Data Quality Assessment, que vaig comprar per tenir una visió més completa a un problema que ja vaig &lt;a href="http://domingosebastian.blogspot.com/2008/03/data-quality.html"&gt;comentar&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Les primeres impressions, basades en els tres primers capítols que formen la primera part "Data Quality Overview", són bones. S'ocupa en aquests capítols, com sol ser habitual, de presentar el problema tractat, mostrant la seva transcendència i donant un marc conceptual per parlar amb propietat de cada aspecte. Espero que la continuació mantengui el nivell.&lt;br /&gt;&lt;br /&gt;Una cosa que he llegit no m'ha agradat massa. Al final del segon capítol escriu que aquest llibre és el primer d'una sèrie, sembla que 4 més, que tractarà del "data quality". Crec que a vegades els temes s'allargen artificialment. Llegirem aquest i ja veurem.&lt;br /&gt;&lt;br /&gt;Ja al començament surten algunes idees interessants. Parla de la necessitat de fer del manteniment de la qualitat de les dades una activitat constant en el projecte i no una acció puntual en un moment donat. Per això, els resultats de l'activitat no ha de ser un document descriguent mancances o unes SQL que corregeixen els valors incorrectes. El resultat ha de ser un sistema executable que ens digui en cada moment quin és l'estat de salut de les nostres dades, juntament amb eines que ens permetin actuar-hi.&lt;br /&gt;&lt;br /&gt;Avui al tren, el meu despatx principal, pensava que ja se poden anar fent tasques d'aquest procés de qualitat en el nostre projecte. Encara no està en producció però ja té dades reals i alguns centres estan en proves. Podríem, per exemple, anar identificant regles de consistència per les nostres dades i implementar vistes que mostrin els registres incorrectes.&lt;br /&gt;&lt;br /&gt;Un cas: comprovar que totes les matèries a les que està matriculat un alumne són possibles donat els estudis als que està matriculat. El programa contempla aquesta regla en les seves pantalles per introduir les dades, però això no ens ha de bastar. Hi pot haver problemes per migracions, modificacions manuals, errades al programa o modificacions en altres parts del programa que no contemplen aquesta regla. La vista que mostri els registres que pateixen aquesta errada podria anar formant part de tot un catàleg amb la idea de poder monitoritzar l'estat de les nostres dades.&lt;br /&gt;&lt;br /&gt;Tornant al llibre, m'ha agradat la definició que fa de la qualitat de les dades: "fitness for the purpose of use". M'agrada per que no és una definició estàtica. Vull dir que unes mateixes dades, sense canviar, poden baixar la seva qualitat si l'aplicació que hi ha per sobre amplia la seva funcionalitat. Aleshores, defectes que abans no importaven ara és manifesten i és necessari corregir los.&lt;br /&gt;&lt;br /&gt;Finalment, pel que duc llegit, també he trobat molt interessat el tractament fet a l'aspecte de l'obligatorietat de certs atributs. Un pot pensar que el tema és trivial. Un camp si és obligatori es posa un not null i sinó no. I ja està. A la pràctica les coses no són tan senzilles.&lt;br /&gt;&lt;br /&gt;Analitzant les entrades podem trobar molts de registres amb valors falsos per un determinat atribut. Per exemple DNI 99999999X. O valors sospitosament repetits. Per exemple, moltes dates de naixement de l'1 de gener de 1970. Aquests valors tenen tot l'aspecte de ser pseudo-nulls per dir-ho així. El problema pot venir de definir un camp com a obligatori quan, a efectes pràctics, s'han de introduir registres sense aquesta informació. Estaríem en un cas de constraint not null equivocada.&lt;br /&gt;&lt;br /&gt;També pot passar a l'enreves. Per exemple tenim 10.000 registres de persones i només 100 tenen el camp telefon a null. Tot fa pensar que es tracta d'un camp que hauria de ser obligatori, i que els 100 registres amb telefon null són incorrectes o incomplets.&lt;br /&gt;&lt;br /&gt;En la nostra aplicació ens hem trobat un cas semblant. Volíem fer que un camp sigués obligatori, però a les bases de dades "legacy" aquest camp era opcional i alguns registres no el tenien. Com ho feim en les migracions? Podríem que el procés de migració s'inseris un valor pseudo-null per aquest camp quan fos null, però ja hem dit que això no és més que un error. Al final optàrem per fer aquest camp opcional a la base de dades i obligatori a la aplicació. No és perfecte però era una solució de compromís. Està subjecte a revisions si ens arriba alguna idea millor.&lt;br /&gt;&lt;br /&gt;Per acabar vull afegir un petit apunt d'àmbit més personal.&lt;br /&gt;&lt;br /&gt;Avui ha deixat el seu càrrec el nostre cap d'àrea, n'Amador Calafat. Durant els dos anys que duc a la conselleria ha estat el tipus de cap que els informàtics volem. Atent a les nostres necessitats i molt pendent que puguessim fer la nostra feina en les millors condicions possibles. No sempre estava a les seves mans arreglar els problemes però per ell no quedava. Apassionat de les noves tecnologies i convençut del benefici que podia aportar el nostre projecte. Motivador i comprensiu amb les necessitats i circumstàncies de cada un de nosaltres. No hem vull allargar més ja que poden semblar els tòpics de sempre, i no ho és. Si no ho pensés no ho escriuria.&lt;br /&gt;&lt;br /&gt;Des d'aquí desitjar-li molta sort en la seva nova etapa.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-3709190487871197031?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/3709190487871197031/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=3709190487871197031' title='1 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/3709190487871197031'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/3709190487871197031'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2008/05/data-quality-el-llibre.html' title='Data Quality (el llibre)'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-4266892187742296245</id><published>2008-05-06T20:20:00.000+02:00</published><updated>2008-05-06T21:49:07.885+02:00</updated><title type='text'>Universitat, alumnes i docència</title><content type='html'>En &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="1.sc" class="vLwzCe"&gt;Ricardo&lt;/span&gt; feia una &lt;a href="http://gallir.wordpress.com/2008/04/28/sintonizar-universidades-y-empresas-pero-%c2%bfque-debe-saber-un-ingeniero/"&gt;entrada &lt;/a&gt;/ assaig referent a la &lt;span original="correspondencia" haspopup="true" role="wairole:menuitem" tabindex="-1" id="2.sc" class="PMpYeb"&gt;correspondència&lt;/span&gt; (o falta de &lt;span original="correspondencia" haspopup="true" role="wairole:menuitem" tabindex="-1" id="3.sc" class="PMpYeb"&gt;correspondència&lt;/span&gt;) entre els objectius formatius d'una universitat i les necessitats de les empreses.&lt;br /&gt;&lt;br /&gt;Cap al final, identifica una sèrie de problemes causants d'un dèficit en la formació dels alumnes:&lt;br /&gt;&lt;br /&gt;"   1. Falta de &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="4.sc" class="vLwzCe"&gt;motivación&lt;/span&gt; (o de &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="5.sc" class="vLwzCe"&gt;pasión&lt;/span&gt; por la &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="6.sc" class="vLwzCe"&gt;informática&lt;/span&gt;) de los &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="7.sc" class="vLwzCe"&gt;alumnos&lt;/span&gt;, &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="8.sc" class="vLwzCe"&gt;también&lt;/span&gt; de &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="9.sc" class="vLwzCe"&gt;profesores&lt;/span&gt;, &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="10.sc" class="vLwzCe"&gt;pero&lt;/span&gt; &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="11.sc" class="vLwzCe"&gt;fundamentalmente&lt;/span&gt; de los &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="12.sc" class="vLwzCe"&gt;alumnos&lt;/span&gt;, que son los al fin y al cabo los que &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="13.sc" class="vLwzCe"&gt;saldrán&lt;/span&gt; al “mercado laboral”.&lt;br /&gt; 2. Falta de &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="14.sc" class="vLwzCe"&gt;confianza&lt;/span&gt; –o &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="15.sc" class="vLwzCe"&gt;coraje&lt;/span&gt; de jóven– que nos &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="16.sc" class="vLwzCe"&gt;dice&lt;/span&gt; “no &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="17.sc" class="vLwzCe"&gt;hay&lt;/span&gt; &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="18.sc" class="vLwzCe"&gt;misterios&lt;/span&gt;, son &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="19.sc" class="vLwzCe"&gt;sólo&lt;/span&gt; bits y &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="20.sc" class="vLwzCe"&gt;bytes&lt;/span&gt;, si &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="21.sc" class="vLwzCe"&gt;otros&lt;/span&gt; lo &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="22.sc" class="vLwzCe"&gt;pudieron&lt;/span&gt; &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="23.sc" class="vLwzCe"&gt;hacer&lt;/span&gt; &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="24.sc" class="vLwzCe"&gt;yo&lt;/span&gt; &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="25.sc" class="vLwzCe"&gt;también&lt;/span&gt; &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="26.sc" class="vLwzCe"&gt;puedo&lt;/span&gt; hacerlo”. &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="27.sc" class="vLwzCe"&gt;Este&lt;/span&gt; problema no &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="28.sc" class="vLwzCe"&gt;sólo&lt;/span&gt; afecta a &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="29.sc" class="vLwzCe"&gt;cómo&lt;/span&gt; se &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="30.sc" class="vLwzCe"&gt;enfrentan&lt;/span&gt; y &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="31.sc" class="vLwzCe"&gt;resuelven&lt;/span&gt; los &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="32.sc" class="vLwzCe"&gt;problemas&lt;/span&gt; &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="33.sc" class="vLwzCe"&gt;dentro&lt;/span&gt; de una empresa, &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="34.sc" class="vLwzCe"&gt;sino&lt;/span&gt; en que &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="35.sc" class="vLwzCe"&gt;hay&lt;/span&gt; &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="36.sc" class="vLwzCe"&gt;muy&lt;/span&gt; &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="37.sc" class="vLwzCe"&gt;pocos&lt;/span&gt; &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="38.sc" class="vLwzCe"&gt;ingenieros&lt;/span&gt; que inicien &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="39.sc" class="vLwzCe"&gt;proyectos&lt;/span&gt; innovadores por &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="40.sc" class="vLwzCe"&gt;cuenta&lt;/span&gt; &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="41.sc" class="vLwzCe"&gt;propia&lt;/span&gt; y con una &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="42.sc" class="vLwzCe"&gt;visión&lt;/span&gt; “global”, es &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="43.sc" class="vLwzCe"&gt;decir&lt;/span&gt; de &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="44.sc" class="vLwzCe"&gt;vender&lt;/span&gt; en &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="45.sc" class="vLwzCe"&gt;todo&lt;/span&gt; el &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="46.sc" class="vLwzCe"&gt;mundo&lt;/span&gt;. Creo que es la causa &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="47.sc" class="vLwzCe"&gt;fundamental&lt;/span&gt; de la &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="48.sc" class="vLwzCe"&gt;carencia&lt;/span&gt; de &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="49.sc" class="vLwzCe"&gt;ingenieros&lt;/span&gt; &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="50.sc" class="vLwzCe"&gt;informáticos&lt;/span&gt; &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="51.sc" class="vLwzCe"&gt;emprendedores&lt;/span&gt;.&lt;br /&gt; 3. &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="52.sc" class="vLwzCe"&gt;Pretender&lt;/span&gt; que el &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="53.sc" class="vLwzCe"&gt;estado&lt;/span&gt; se &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="54.sc" class="vLwzCe"&gt;haga&lt;/span&gt; &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="55.sc" class="vLwzCe"&gt;cargo&lt;/span&gt; del &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="56.sc" class="vLwzCe"&gt;entrenamiento&lt;/span&gt; &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="57.sc" class="vLwzCe"&gt;profesional&lt;/span&gt;. Es un problema empresarial, la &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="58.sc" class="vLwzCe"&gt;competencia&lt;/span&gt;, el &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="59.sc" class="vLwzCe"&gt;outsourcing&lt;/span&gt;, la falta de &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="60.sc" class="vLwzCe"&gt;mercado&lt;/span&gt; &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="61.sc" class="vLwzCe"&gt;hace&lt;/span&gt; que &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="62.sc" class="vLwzCe"&gt;parezca&lt;/span&gt; &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="63.sc" class="vLwzCe"&gt;muy&lt;/span&gt; caro o &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="64.sc" class="vLwzCe"&gt;imposible&lt;/span&gt; que las &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="65.sc" class="vLwzCe"&gt;empresas&lt;/span&gt; se &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="66.sc" class="vLwzCe"&gt;hagan&lt;/span&gt; &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="67.sc" class="vLwzCe"&gt;cargo&lt;/span&gt; del &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="68.sc" class="vLwzCe"&gt;entrenamiento&lt;/span&gt; &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="69.sc" class="vLwzCe"&gt;específico&lt;/span&gt;, y &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="70.sc" class="vLwzCe"&gt;luego&lt;/span&gt; de la &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="71.sc" class="vLwzCe"&gt;formación&lt;/span&gt; continua, que &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="72.sc" class="vLwzCe"&gt;necesitan&lt;/span&gt; los &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="73.sc" class="vLwzCe"&gt;nuevos&lt;/span&gt; &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="74.sc" class="vLwzCe"&gt;ingenieros&lt;/span&gt;.&lt;br /&gt;"&lt;br /&gt;&lt;br /&gt;Com a estudiant (&lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="75.sc" class="vLwzCe"&gt;mediocrillo&lt;/span&gt;) i treballador (no m'avaluaré ja que hem llegeixen els &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="76.sc" class="vLwzCe"&gt;jefes&lt;/span&gt;) no estic molt d'acord amb aquests punts. O més ben dit, els veig més com a &lt;span original="consequències" haspopup="true" role="wairole:menuitem" tabindex="-1" id="77.sc" class="PMpYeb"&gt;conseqüències&lt;/span&gt; i no tant com a causes.&lt;br /&gt;&lt;br /&gt;Son deficiències en la formació a la universitat les que fan als alumnes desmotivats, &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="78.sc" class="vLwzCe"&gt;falts&lt;/span&gt; de confiança i incapaços de &lt;span original="mantenirse" haspopup="true" role="wairole:menuitem" tabindex="-1" id="79.sc" class="PMpYeb"&gt;mantenir se&lt;/span&gt; actualitzats.&lt;br /&gt;&lt;br /&gt;Crec que hi ha una concepció equivocada amb el que és la &lt;span original="excelència" haspopup="true" role="wairole:menuitem" tabindex="-1" id="80.sc" class="PMpYeb"&gt;excel·lència&lt;/span&gt; docent. Se defineix un cos de coneixements gran i complex, unes pràctiques molt exigents i una avaluació dura. El resultat és la necessitat d'invertir un esforç molt gran per assolir la titulació, el qual deriva (suposem) directament amb un gran valor d'aquesta. Que té de &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="81.sc" class="vLwzCe"&gt;bò&lt;/span&gt; això? molt, podem pensar. Ens ha costat molt, per tant, segur que hem après molt. A més, molts han "caigut" pel camí, per tant, &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="82.sc" class="vLwzCe"&gt;esteim&lt;/span&gt; en una posició privilegiada a l'hora de competir per els millors llocs. Tot &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="83.sc" class="vLwzCe"&gt;maravallos&lt;/span&gt;, no?&lt;br /&gt;&lt;br /&gt;A la pràctica, això no és així. Aquests requeriments, si no van acompanyats d'un fort element &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="84.sc" class="vLwzCe"&gt;motivador&lt;/span&gt;, simplement cremen. A més, el model d'avaluació fa que el més rentable sigui adoptar &lt;span original="actituts" haspopup="true" role="wairole:menuitem" tabindex="-1" id="85.sc" class="PMpYeb"&gt;actituds&lt;/span&gt; orientades a la superació de les proves i no a la comprensió profunda de la matèria. Creen a l'alumne estratega. Gran rendiment que no va acompanyat de cap interès &lt;span original="genui" haspopup="true" role="wairole:menuitem" tabindex="-1" id="86.sc" class="PMpYeb"&gt;genuí&lt;/span&gt; per la &lt;span original="materia" haspopup="true" role="wairole:menuitem" tabindex="-1" id="87.sc" class="PMpYeb"&gt;matèria&lt;/span&gt;. Aprovat? sí, &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="88.sc" class="vLwzCe"&gt;idò&lt;/span&gt; a guardar els apunts a la caixa i a per la següent.&lt;br /&gt;&lt;br /&gt;Aquest alumne "estratega" com serà? motivat? &lt;span original="dificilment" haspopup="true" role="wairole:menuitem" tabindex="-1" id="89.sc" class="PMpYeb"&gt;difícilment&lt;/span&gt;. Amb molta confiança? sí, però just per els problemes que li posin a la taula, no per fer &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="90.sc" class="vLwzCe"&gt;d'emprenador&lt;/span&gt;. Autodidacta? podeu posar la ma al foc que no.&lt;br /&gt;&lt;br /&gt;Si ens surt un alumne així, la culpa possiblement sigui seva. Si la majoria són així, la culpa és més "ambiental". Pot ser sigui el clima, pot ser el que &lt;span original="menjen" haspopup="true" role="wairole:menuitem" tabindex="-1" id="91.sc" class="PMpYeb"&gt;mengen&lt;/span&gt; o pot ser sigui la &lt;span original="univeritat" haspopup="true" role="wairole:menuitem" tabindex="-1" id="92.sc" class="PMpYeb"&gt;universitat&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;La tasca del professor és clau per obtenir un altre tipus d'alumne. S'ha de &lt;span original="formentar" haspopup="true" role="wairole:menuitem" tabindex="-1" id="93.sc" class="PMpYeb"&gt;fomentar&lt;/span&gt; un aprenentatge profund, natural, no forçat.&lt;br /&gt;Que vol dir això? El que és clar és el que no vol dir. No és "&lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="94.sc" class="vLwzCe"&gt;transmetre&lt;/span&gt;" un conjunt de coneixements. Tampoc és convertir a l'alumne amb un &lt;span original="autòmata" haspopup="true" role="wairole:menuitem" tabindex="-1" id="95.sc" class="PMpYeb"&gt;autòmat&lt;/span&gt; que resol problemes.&lt;br /&gt;&lt;br /&gt;Acabada la universitat he agafat molt de gust per la lectura de llibres tècnics. En ells veig als autors explicant per que els problemes tractats m'han de semblar importants i propis, per que creuen que aproximacions anteriors no funcionaven del tot bé, i, sobretot, entenc que aquesta nova &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="96.sc" class="vLwzCe"&gt;tentativa&lt;/span&gt; només és això, un nou intent en un camí no acabat. El que m'ensenya l'autor me és interessant, real, natural i gairebé sempre me deixa amb ganes de més. És el que m'agradaria &lt;span original="haber" haspopup="true" role="wairole:menuitem" tabindex="-1" id="97.sc" class="PMpYeb"&gt;haver&lt;/span&gt; trobat a la universitat.&lt;br /&gt;&lt;br /&gt;Pels professors, recomanar el llibre "Lo que &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="98.sc" class="vLwzCe"&gt;hacen&lt;/span&gt; los &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="99.sc" class="vLwzCe"&gt;mejores&lt;/span&gt; &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="100.sc" class="vLwzCe"&gt;profesores&lt;/span&gt; &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="101.sc" class="vLwzCe"&gt;universitarios&lt;/span&gt;" de &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="102.sc" class="vLwzCe"&gt;Ken&lt;/span&gt; &lt;span haspopup="true" role="wairole:menuitem" tabindex="-1" id="103.sc" class="vLwzCe"&gt;Bain&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-4266892187742296245?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/4266892187742296245/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=4266892187742296245' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/4266892187742296245'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/4266892187742296245'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2008/05/universitat-alumnes-i-docncia.html' title='Universitat, alumnes i docència'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-7422960320054980527</id><published>2008-04-26T16:37:00.000+02:00</published><updated>2008-04-26T16:49:20.548+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='cursos'/><category scheme='http://www.blogger.com/atom/ns#' term='eines'/><category scheme='http://www.blogger.com/atom/ns#' term='docbook'/><title type='text'>DocBook vs. Open Office</title><content type='html'>Ja fa molt de temps que estic emprant com a sistema per escriure la documentació el &lt;a href="http://docbook.org/"&gt;docbook&lt;/a&gt;. Aquest és un estàndard XML que defineix com estructurar el contingut d'un document, principalment tècnic i que alguns dels grans projectes en codi lliure.&lt;br /&gt;&lt;br /&gt;El motius que me dugueren a emprar-ho, en la documentació del projecte final de carrera i després en la elaboració dels manuals per els cursos foren diversos.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Es un format de text, per tant&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;ul&gt;&lt;li&gt;Si passa alguna cosa estranya, sempre pots veure l'origen del problema&lt;/li&gt;&lt;li&gt;Se integra de forma natural en el sistema de control de versions&lt;/li&gt;&lt;li&gt;Si has de fer una correcció petita, ho pots fer en qualsevol editor&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;Al emprar XML, afegim:&lt;/li&gt;&lt;ul&gt;&lt;li&gt;Tens les regles de validació per tenir el document ben format&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Pots emprar tecnologies com XInclude per dividir el document en mòduls i formar combinacions diverses (importantíssim)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Quan treballes en el document, et centres exclusivament en el contingut, sense despistar-te en la presentació&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Al tenir la presentació en les plantilles XSLT, es trivial disposar de distintes versions de cada document en distints formats&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;Al emprar Docbook, afegeix a l'anterior:&lt;/li&gt;&lt;ul&gt;&lt;li&gt;Tens un model semàntic interessant per construir el teu document: codi, noms de classes, acrònims, referències ...&lt;/li&gt;&lt;li&gt;Disposes d'editors XML específics per aquesta aplicació&lt;/li&gt;&lt;li&gt;Disposes de plantilles de presentació ja fetes i molt correctes&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;Tot això ha anat molt bé i cada una de les possibilitats llistades anteriorment ha solucionat problemes molt reals.&lt;br /&gt;&lt;br /&gt;Amb el temps, però, han sortit algunes deficiències per necessitats concretes que m'han demanat. Un, per exemple, és el format amb capçaleres i logos que me demanen a la CAEB per els manuals dels cursos. Normalment parteixo de les plantilles disponibles de XSL i només modifico alguns dels paràmetres definits. Incursions als detalls de les transformacions per controlar aquests aspectes superen els meus coneixements, molt bàsics, de XSLT i FO.&lt;br /&gt;Això implica que la part final del procés de construir el document, era generar a partir del docbook un RTF, que finalment obria al word i acabava de "formatejar". Sí, tant malament com sona.&lt;br /&gt;&lt;br /&gt;Estava disposat a conviure amb aquest problema quan l'altra dia, durant la part d'exercicis en el curs de programació amb Servlets i JSPque estic fent, vaig notar que alguna cosa no estava del tot bé. Estava mirant el manual d'un alumne, cercant un exemple de codi quan me va parèixer veure el text una mica "toston". El format està molt bé per una documentació de referència, però un manual de curs ha de ser, trobo, més visual. Una referència la llegeixes 30 vegades, amb detall, esperant que surti tot. Un manual de curs ha de saltar tot a la vista, centrar-se amb els aspectes fonamentals, ajudar-se molt dels aspectes visuals i deixar la resta de banda. Hi ha moltíssims llibres bons damunt qualsevol tema i aquest manual no està, de cap manera, per competir amb ells. I tot el que diem del manual, val encara molt més per les presentacions.&lt;br /&gt;&lt;br /&gt;Això hem va fer pensar que pot ser, després de tant de temps, calia considerar un canvi. Per alguns texts, la part de presentació no és un tema marginal o separat del contingut, sinó que les dues coses en formen una. Per això, provaré de fer (refer) el manual d'un curs d'eines emprant el Open Office. Així podré avaluar millor quin camí seguir.&lt;br /&gt;&lt;br /&gt;No han de fer peresa els canvis! i, si un ho diu a la feina, ha de ser coherent a ca seva!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-7422960320054980527?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/7422960320054980527/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=7422960320054980527' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/7422960320054980527'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/7422960320054980527'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2008/04/docbook-vs-open-office.html' title='DocBook vs. Open Office'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-3605236306639182517</id><published>2008-04-20T16:34:00.000+02:00</published><updated>2008-04-20T16:42:33.993+02:00</updated><title type='text'>Curs d'aplicacions Web J2EE</title><content type='html'>Demà comença el curs de aplicacions web amb J2EE. Les tecnologies explicades estan basades en els requeriments de la DGTIC per desplegar aplicacions en els seus servidors.&lt;br /&gt;&lt;br /&gt;En el contingut està inclòs el detall de les APIs dels Servlets, filtres etc, elements del web.xml, aspectes bàsics del JBoss, JSP, JSTL i semblants.&lt;br /&gt;&lt;br /&gt;Tot això és bo d'explicar i entendre, i hi ha exercicis per anar provant-ho. El que m'agradaria, per estar realment satisfet a la finalització del curs és que els alumnes haguessin après i entès altres coses més pròpies de l'experiència que de la memorització:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Entendre com l'API dels Servlets encaixa en el funcionament del protocol HTTP. Respectar l'especificació HTTP i no anar "contra ella"&lt;/li&gt;&lt;li&gt;Per cada un dels elements de la API, poder donar un exemple de problema que soluciona. Fer de cada un dels mètodes explicats, un recurs que quedi a la seva "caixa d'eines" encara que s'hagi de repassar la forma exacte d'emprar-se.&lt;/li&gt;&lt;li&gt;Haver reflexionat sobre els aspectes transversals que afecten al sistema i com els filtres poden atacar parts d'aquests.&lt;/li&gt;&lt;li&gt;Diferències claus entre una solució programàtica i una declarativa. Per exemple, referent a la seguretat, tractament d'errors etc.&lt;/li&gt;&lt;li&gt;Diferències claus entre una solució stateless i una stateful. Us de la sessió. Avantatges i inconvenients de fer-ho.&lt;/li&gt;&lt;li&gt;Importància de veure les URLs de l'aplicació com una API més. Un cas molt il·lustrador és l'aproximació RESTful&lt;/li&gt;&lt;li&gt;Limitacions de les tecnologies explicades i vies de solució. Sobretot el patró MVC.&lt;/li&gt;&lt;li&gt;Rol de la capa de presentació en la resta de l'arquitectura del sistema. Que convé que faci i que no. Opcions segons la complexitat del sistema a construir.&lt;/li&gt;&lt;li&gt;Conèixer per on començar a cercar quan tenim un problema i no quedar bloquejats. On son els logs, intentar reproduir l'error, veure els casos més habituals etc.&lt;/li&gt;&lt;/ul&gt;Algún expert en la part web té alguna idea més per destacar? Qué demanarieu a un candidat per la vostra empresa que hagués de fer Servlets i JSPs.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-3605236306639182517?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/3605236306639182517/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=3605236306639182517' title='1 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/3605236306639182517'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/3605236306639182517'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2008/04/curs-daplicacions-web-j2ee.html' title='Curs d&apos;aplicacions Web J2EE'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-5230584532093734604</id><published>2008-04-18T21:13:00.000+02:00</published><updated>2008-04-18T21:17:51.771+02:00</updated><title type='text'>the career programmer</title><content type='html'>Imagineu que comprau un llibre sobre el món de la programació i veieu, en una pàgina qualsevol, això:&lt;br /&gt;&lt;br /&gt;"Possessing no social skills that I've ever been able to discern, the arrogant big head is almost always an absolute killer programmer, the best of the best. The problem with this guy is not just his cocky, condescending, and demeaning attitude, but the fact that 9.990872 times out of 10 he can back it up. He is just that good."&lt;br /&gt;&lt;br /&gt;I el llibre és això, un paràgraf darrera l'altra com aquest, durant 250 pàgines. Quin desastre.&lt;br /&gt;&lt;br /&gt;El títol de l'engendre es "The career programmer" i el geni és Christopher Duncan. No sé per quin motiu ho vaig comprar. No tenia cap llibre de la appres i volia provar-ho. Era una segona edició, per tant alguna cosa bona havia de tenir ... &lt;br /&gt;&lt;br /&gt;Quasi sempre procuro llegir un capítol d'un llibre abans de comprar-ho. Es una forma bastant segura de invertir bé els doblers. També hem guio per les opinions de gent que respecto i, en un grau menor, el "Customers Who Bought This Item Also Bought..." d'amazon. En aquest cas no vaig fer res d'això.&lt;br /&gt;&lt;br /&gt;El meu projecte &lt;a href="http://domingoseb.balearweb.net/post/12782"&gt;d'escriure un llibre&lt;/a&gt; torna a prendre força ;-)&lt;br /&gt;&lt;br /&gt;Avisats quedau!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-5230584532093734604?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/5230584532093734604/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=5230584532093734604' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/5230584532093734604'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/5230584532093734604'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2008/04/career-programmer.html' title='the career programmer'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-8585271019172224081</id><published>2008-04-15T21:39:00.000+02:00</published><updated>2008-04-15T21:44:05.443+02:00</updated><title type='text'>Errades</title><content type='html'>A ningú ens agrada cometre errades. Quan en el nostre codi en trobem una el que feim és arreglar-la el més aviat possible i "commitem" els canvis. Amb un poc de sort, pocs usuaris es veuran afectats i les dades patiran poc o gens fins restaurar-se a l'estat correcte.&lt;br /&gt;&lt;br /&gt;Moltes errades podríem qualificar-les de "tontes". Les que es produïxen quan ens equivoquem amb una expressió booleana, oblidem posar alguna condició senzilla sobre els paràmetres etc. Son els casos on teníem "al cap" la solució correcte només que errem a l'hora d'expressar-ho en codi.&lt;br /&gt;&lt;br /&gt;Aquests tipus d'errors son els que podem eliminar just al moment que programem amb l'ús de proves unitàries. I això és un factor a tenir molt en compte: el fet que siguin "tontes" no vol dir que no ens puguin donar malts de cap importants si arriben al sistema en producció.&lt;br /&gt;&lt;br /&gt;En el moment de corregir l'errada, ens hauríem de demanar si la qualitat del codi ha propiciat que es produeixi aquesta. Pot ser un mètode massa llarg o complexe, o les variables no tenen un nom adient, o el mètode tracta molts aspectes diferents. Tot això fa que la probabilitat de errades augmentin. El fet que se n'hagui produït una o més és una senyal de que pot ser sigui necessari refactoritzar aquesta part de codi.&lt;br /&gt;&lt;br /&gt;Llavors hi ha altres errades que surten quan, tal com ens diuen els usuaris, el sistema no se comporta per un determinat cas com hauria de fer-ho. O, sense ser una errada te els mateixos efectes, és necessari afegir una nova funcionalitat al sistema. Els típics canvis en els requeriments que tant contents ens solen fer.&lt;br /&gt;&lt;br /&gt;Aquí la situació és diferent. Ja no ens hem equivocat expressant en codi la nostra idea del sistema, sinó que l'errada estava en la nostra idea mateixa. En aquest cas, el camí habitual sol ser fer "els mínims canvis possibles" i intentar proporcionar, si és possible, els resultats que sol·licita l'usuari, a partir del model que implementa la nostra aplicació.&lt;br /&gt;&lt;br /&gt;Aquesta no és una mala opció si és necessari donar una resposta ràpida. Però fer-ho així implica perdre oportunitats. El nostre model inicial mai serà perfecte, ni tampoc el segon ni el tercer .... Tenim tendència a defensar visceralment el model que tanta feina ens ha duit construir i negociar amb els companys. Tendim a veure la situació com una batalla entre el nostre model lògic i enraonat, i els canvis capritxosos dels usuaris.&lt;br /&gt;&lt;br /&gt;Amb aquesta postura difícilment aconseguirem que la nostra aplicació evolucioni tal com augmenta el nostre coneixement en l'àmbit en que funciona la nostra aplicació.&lt;br /&gt;&lt;br /&gt;Cada vegada que es produeix una situació així, encara que no tinguem temps per implementar-ho, convé intentar entendre bé els motius pels quals l'usuari diu que ho necessita. Estudiar quins canvis hauríem de fer al nostre model (encara que no ho facem ara) i mirar en quines altres situacions aquest "desajust" es pot manifestar.&lt;br /&gt;&lt;br /&gt;De fet, pot ser seria interessant fer una plànol del sistema, semblant a quan surt a les pelis un amb incidències d'una epidèmia terrible, i observar quins mòduls acumulen més errades de qualsevol tipus. Segur que aquests mòduls tenen molts nombres per necessitar una refactorització o un remodelat complet.&lt;br /&gt;&lt;br /&gt;Encara que un pot objectar:&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt; Si els mòduls "son" d'un o d'uns pocs desenvolupadors, no es gaire agradable tenir exposats públicament les errades&lt;/li&gt;&lt;br /&gt;&lt;li&gt; No fa falta el plànol i xinxetes vermelles, basta un informe del trac o similar. Cert, però que voleu que us digui, només cerco una excusa per fer com el científic de les pel·lícules.&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;El que vull dir és que encara que vegem en les errades uns "bitxos" molestos que provoquen úlceres, també s'han de veure com a indicadors que apunten al camí que han de seguir els canvis al sistema per a que evolucioni de forma sana i sigui cada cop més útil i fàcil de mantenir.&lt;br /&gt;&lt;br /&gt;Mode publicitat on:&lt;br /&gt;M'han dit que queda una plaça lliure per el curs que donem durant la setmana vinent i tres dies de l'altre. El curs es de Servlets i JSPs, a la CAEB, de 16:00 a 19:00. És gratuït i puc afirmar amb bastant confiança que la relació qualitat / preu és acceptable ;-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-8585271019172224081?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/8585271019172224081/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=8585271019172224081' title='1 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/8585271019172224081'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/8585271019172224081'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2008/04/errades.html' title='Errades'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-2439812310638346678</id><published>2008-04-13T09:36:00.000+02:00</published><updated>2008-04-13T09:42:04.598+02:00</updated><title type='text'>Python, Django , Google App Engine  ... opinions d'un programador Java</title><content type='html'>Calia estar en una cova per tal de no assabentar-se del que ha estat el gran aconteixament d'aquests darrers dies: el Google app engine. Vista la demostració que fan en vídeo de la facilitat amb que es crea una aplicació, es prova i es desplega, només puc qualificar-la d'espectacular. Cap línia de codi que sigui renou, cap instant perdut amb res improductiu, cap espera ... cap de les coses que si feim feina amb altres sistemes poden semblar inevitables.&lt;br /&gt;&lt;br /&gt;A tot això cal dir que aquest llançament només ha estat una empenta a un moviment ja prou fort. Del Django només en sento parlar bé darrerament i el Python ja fa molts d'anys que té una comunitat molt fidel.&lt;br /&gt;&lt;br /&gt;La situació que pot venir, sembla que sigui un creixement d'aquest model de programació a costa de reduir la presència de les altres alternatives existents, principalment el Java.&lt;br /&gt;&lt;br /&gt;Com a programador en Java, això podria suposar una gran preocupació per la pèrdua de valor que poden patir els coneixaments adquirits durant tots els darrers anys de feina. Després de tants d'esforços, tornar al nivell de "novato" que tenia fa uns 8 o 9 anys, al començar amb el Java.&lt;br /&gt;&lt;br /&gt;El dia que això sigui així, serà moment de vendre la casa, deixar la feina i els cursos, i cercar-me una altre forma de vida que no sigui la de programador.&lt;br /&gt;&lt;br /&gt;I és que això és l'important, un és primer programador i després programador en el que sigui. Ja vam fer el bot de PL/SQL a Java i es clar que es poden fer els que sigui.&lt;br /&gt;&lt;br /&gt;Però abans de matar el Java, anem a pensar el que pot passar (no ho endevinarem, mai ho feim, però això no fa inútil el que hi pensem). El Java és un gran llenguatge. A mi particularment m'entusiasma. Però no és perfecte ni com a llenguatge ni, molt menys, com a entorn de desenvolupament d'aplicacions que interaccionen amb altres tecnologies (principalment web i base de dades relacionals).&lt;br /&gt;&lt;br /&gt;Fa uns anys, semblava que el Java s'acomodava en les seves limitacions ja que no li convenia fer grans canvis que molestessin a la seva enorme base d'usuaris. Però llavors sortí el .Net, gran amenaça que havia de fer desapereixer Java del mapa (no llegigeiu aquí cap referencia indirecta la Python). Això obligà a la comunitat Java a adaptar-se, treure-se la por del damunt i ampliar el llenguatge en formes que durant molts d'anys s'havien resistit a introduir-se per por. La competència és bona, i el Java d'avui és millor que el de fa 3 o 4 anys.&lt;br /&gt;&lt;br /&gt;El Python, Django, Google app engine i companyia poden forçar al Java a atacar sense complexes i amb eficàcia les parts més febles que es duien reconeguent durant els darrers anys.&lt;br /&gt;&lt;br /&gt;I, si no ho aconsegueix, aleshores adéu Java i ben vingut l'entorn que ho millora.&lt;br /&gt;&lt;br /&gt;Abans d'enterrar a ningú, però, crec que s'ha de considerar el que és la gran tasca de la programació. Programar és gestionar la complexitat (perdoneu que no recordi l'autor de la cita). Gestionar la complexitat és actuar en la linia "Make everything as simple as possible, but not simpler." (aquesta sí la sé: Albert Einstein).&lt;br /&gt;&lt;br /&gt;Un exemple : surt una tecnologia senzillíssima, HTML, i tot-hom que sap escriure en un teclat esdevé "programador". Anys després, l'HTML ja no és tan senzill. Un ús correcte de l'HTML, el CSS i, si escau, el JavaScript, requereix d'uns coneixèments teòrics no gens trivials. Aquesta complexitat afegida a la tecnologia no és gratuïta. És necessari "complicar" les eines per poder escalar en la complexitat del producte a construir.&lt;br /&gt;&lt;br /&gt;Si vols fer una tenda de campanya, poses uns pals i una tela. Si vols ver un gratacels, comences fent un forat!&lt;br /&gt;&lt;br /&gt;Java és un gran llenguatge per implementar models complexes. Alguns dels seus afegits (Spring, Hibernate ...) són, en distinta mesura, "complicacions" que no troben una explicació en aplicacions que es puguin fer en 10 minuts. JDBC i codi Java directe és molt més fàcil. La seva raó de ser està en els sistemes lluny de ser trivials.&lt;br /&gt;&lt;br /&gt;El que vull dir, és que el camí a seguir no és la via fàcil de retirar tot el que és complexe, i fer del desenvolupament en Java una cosa que es pugui ensenyar en 3 dies. El que s'ha de fer, i si no ho fa s'el menjaran amb patates, és filar molt prim i sense pietat, i per cada tema, afrontar la difícil qüestió de fer-ho "as simple as possible, but not simpler."&lt;br /&gt;&lt;br /&gt;I, per això, grans avanços com els Django i companyia han de servir com a models i fonts d'idees.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-2439812310638346678?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/2439812310638346678/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=2439812310638346678' title='4 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/2439812310638346678'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/2439812310638346678'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2008/04/python-django-google-app-engine.html' title='Python, Django , Google App Engine  ... opinions d&apos;un programador Java'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-6971954266878710131</id><published>2008-03-22T16:21:00.000+01:00</published><updated>2008-03-22T16:28:40.852+01:00</updated><title type='text'>Data quality</title><content type='html'>Hi ha un tema que aquests darrers dies m'ha estat voltant molt pel cap: com podem mantenir la qualitat de la informació de les nostres bases de dades.&lt;br /&gt;&lt;br /&gt;L'origen d'aquesta qüestió ha estat que bona part de les tasques que he de realitzar  l'aplicació que tenim en producció està relacionada amb mantenir les dades: entrades d'usuaris incorrectes, migracions que s'han de "depurar" manualment, etc.&lt;br /&gt;&lt;br /&gt;L'aplicació que tenim ara en desenvolupament tendrà un volum d'informació dos ordres de magnitut superior, i per això s'ha d'intentar aprendre del que hi ha fet.&lt;br /&gt;&lt;br /&gt;Se m'ocorren algunes coses que ja se poden anar incloguent.&lt;br /&gt;&lt;br /&gt;Tenir processos que detectin informació incompleta que queda com a brutícia al sistema. Moltes funcions d'usuari requereixen de distintes passes i sovint s'abandonen abans d'acabar, deixant dades incompletes al sistema. Si s'aconsegueix implementar detectors d'aquestes dades, se pot eliminar moltíssima basura.&lt;br /&gt;&lt;br /&gt;Permetre "fusionar" entrades a la base de dades que corresponen al mateix concepte. Dos usuaris entren registres que corresponen a la mateixa persona, empresa etc. Fusionar aquests registres no es trivial si molta informació depen d'ells.&lt;br /&gt;&lt;br /&gt;Detectar problemes d'integritat que no estan contemplats per les regles de la base de dades. Sovint venen per deixar modificar relacions un cop creats els objectes, fora del fluxe normal, o per modificacions manuals a la base de dades.&lt;br /&gt;&lt;br /&gt;Comprobar la completesa de la informació proporcionada per cada usuari. Les restriccions "not-null" de la base de dades son un requeriment molt bàsic que s'ha de completar amb altres avisos a nivell d'aplicació per l'usuari.&lt;br /&gt;&lt;br /&gt;De fet, bona part de la tasca només té sentit si la podem delegar en l'ususari. Amb centenars o milers d'usuaris emprant i introduint informació, l'efectivitat de tot això només serà acceptable si la responsabilitat no cau en els 5 o 8 desenvolupadors sinó en tots els usuaris. Clar que s'els hi ha de facilitar aquesta tasca.&lt;br /&gt;&lt;br /&gt;Per exemple, periòdicament s'els hi pot mostrar un llistat d'avisos en entrar a l'aplicació amb l'estil:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;No s'ha introduit qui es el responsable de l'activitat A. Aneu aquí per fer-ho ara.&lt;/li&gt;&lt;li&gt;S'han definit 3 grups que no tenen cap persona associada. Per revisar-los o esborrar-los aneu aquí.&lt;/li&gt;&lt;li&gt;etc. etc.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Això coloca la responsabilitat de mantenir la qualitat de les dades al lloc que correspon: el que les introdueix i empra. Ara bé, hi ha una altra responsabilitat que és fer el més fàcil possible que els usuaris puguin cumplir la seva part i això ens correspon als desenvolupadors.&lt;br /&gt;&lt;br /&gt;Tot plegat m'ha duit a mirar una mica per la wikipedia i per amazon. Sembla que el tema està englobat baix el que s'anomena "data quality" i hi ha alguns &lt;a href="http://www.amazon.com/Data-Quality-Assessment-Arkady-Maydanchik/dp/0977140024/"&gt;llibres interessants&lt;/a&gt;. Pot ser serà el proper que caurà...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-6971954266878710131?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/6971954266878710131/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=6971954266878710131' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/6971954266878710131'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/6971954266878710131'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2008/03/data-quality.html' title='Data quality'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-6898513769442576840</id><published>2008-03-09T19:41:00.000+01:00</published><updated>2008-03-09T19:43:46.998+01:00</updated><title type='text'>Comentaris al llibre Interface-Oriented Design</title><content type='html'>Vaig comprar el llibre degut a la qualitat dels llibres del "Pragmatic bookshelf". També el tema m'atreia ja que estic convençut que les interfícies son claus en el disseny de l'aplicació per aconseguir els objectius tan comentats ( escalabilitat, modularitat etc). De l'autor Ken Pugh no havia llegit res.&lt;br /&gt;&lt;br /&gt;Interface-Oriented Design comença amb exemples d'interfícies que ens podem trobar en el mon real, amb les interaccions amb proveïdors de serveis com poden ser les tendes de pizzes. A partir d'aquests exemples ve a definir els aspectes fonamentals que compleixen aquestes entitats.&lt;br /&gt;&lt;br /&gt;Defineix, inspirat amb les regles robòtiques de Isaac Asimov, tres lleis (The Three Laws of Interfaces) que han de complir les interfícies (avís de traducció poc acurada):&lt;br /&gt;&lt;ol&gt;&lt;li&gt; La implementació d'una interfície ha de fer el que els seus mètodes diuen.&lt;/li&gt;&lt;li&gt;La implementació d'una interfície no ha de fer mal.&lt;/li&gt;&lt;li&gt;Si una implementació no pot complir la seva responsabilitat, ho ha de notificar al client.&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;Per il·lustrar aquestes normes, empra les comandes Unix per la gestió de fitxers.&lt;br /&gt;&lt;br /&gt;A continuació entra a descriure aspectes que ha s'han de balancejar a l'hora de definir una interfície ( stateless vs. stateful, orientació a dades o a serveis, etc), mostrant els pros i els contres de cada un.&lt;br /&gt;&lt;br /&gt;Finalitza la part general amb un tractament dels principis "high cohesion, low coupling" aplicats a les interfícies i a consideracions especials per les interfícies remotes.&lt;br /&gt;&lt;br /&gt;La segona part, d'un sol capítol està dedicada a la visió del procés de desenvolupament adoptada per les metodologies àgils, amb la inclusió del èmfasi en l'ús d'interficies per la fase d'anàlisi-disseny. Introdueix la definició d'unes targes (IRI cards - Interface-Responsability-Interaction) per mostrar a alt nivell les principals interfícies i les seves interaccions.&lt;br /&gt;&lt;br /&gt;La tercera part està dedicada a un parell de casos pràctics que mostren l'aproximació recomanada en el llibre en la resolució de problemes concrets.&lt;br /&gt;&lt;br /&gt;La meva opinió és que es tracta d'un llibre prescindible en l'àmbit del disseny de sistemes complexes. No veig cap aportació clara en la construcció del model de domini que no hi hagi, per exemple, al Domain Driven Design d'Eric Evans o al Pattern of Enterprise Applications Architecure de Martin Fowler. Tampoc a nivell de metodologia, que tracta a nivell superficial, és comparable a llibres de metodologies àgils, extreme programming o Test Driven Development. La part final, per mi la més profitosa, mostra alguns dissenys interessants per problemes d'aparença bastant real.&lt;br /&gt;&lt;br /&gt;Tampoc vull dir que sigui un mal llibre. Està ben escrit, el tema és interessant i es veu que hi ha al darrera un autor amb amplis coneixements. El problema és que es un llibre curt que tracta un tema molt ampli i molt explorat per la literatura actual. Altres llibres de format semblant de la col·lecció The Pragmatic Programmers encerten més en la definició del àmbit a tractar i aconseguixen aportar una visió dinàmica i fresca.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-6898513769442576840?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/6898513769442576840/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=6898513769442576840' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/6898513769442576840'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/6898513769442576840'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2008/03/comentaris-al-llibre-interface-oriented.html' title='Comentaris al llibre Interface-Oriented Design'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-6423869519834525183</id><published>2008-03-02T18:10:00.000+01:00</published><updated>2008-03-02T19:03:18.569+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='eines'/><title type='text'>Eines de control de qualitat</title><content type='html'>Quan anam programant és important anar revisant la feina per tal d'eliminar els defectes de disseny o implementació que introduïm. Refactoritzacions freqüents ajuden a mantenir el codi amb la qualitat suficient per a fer-ho manejable.&lt;br /&gt;&lt;br /&gt;Com en altres aspectes de la nostra feina, disposar de les eines que ens ajudin a assolir aquestes qualitats per les nostres aplicacions és clau.&lt;br /&gt;&lt;br /&gt;Trobar eines que ens ajudin a escriure codi, compilar-lo o executar-lo és trivial. Tampoc és complicat emprar eines que construeixin l'aplicació, gestionin les incidències o tenguin cura de la gestió del codi font. Ara bé, que ens ajudin a mantenir la qualitat del codi i del disseny sembla un tema apart. Com ens pot ajudar una eina?. Segur que tots hem viscut discussions menys o més fortes per com modelar en entitat-relació una determinada informació, o com distribuir distintes classes en els mòduls que tenim. No sembla haver-hi gaires aspectes qualitatius que permetin emprar una eina per ajudar-nos.&lt;br /&gt;&lt;br /&gt;Evidentment n'hi ha i bastants. Basta observar com habitualment els editors (jo empro l'IntelliJ, però segur que el vostre també ho fa) ens avisen de defectes del codi: variables no emprades, codi mort, estructures innecessàriament complexes i moltes altres. Aquests són els problemes més fàcils però no els únics.&lt;br /&gt;&lt;br /&gt;La darrera setmana vaig estar provant el &lt;a href="http://www.clarkware.com/software/JDepend.html"&gt;JDepend&lt;/a&gt;. Té moltes funcionalitats però estava interessat especialment en evitar cicles de dependències entre els paquets. Curiosament, els que va detectar varen ser deguts a decisions que ja ens havien agradat molt poc i no hi havia hagut gaire consens. A l'arsenal ja disposem de fa temps del &lt;a href="http://pmd.sourceforge.net/"&gt;PMD &lt;/a&gt;que ens avisa de recursos no alliberats correctament, d'usos poc correctes del classloaders i moltes coses més. El &lt;a href="http://findbugs.sourceforge.net/"&gt;findbugs&lt;/a&gt; ens posa en evidència quan no implementem correctament la tupla equals-hashcode i altres "novatades" per l'estil.&lt;br /&gt;&lt;br /&gt;Com sempre, l'advantatge d'aquestes eines està subjecte al fet que s'emprin habitualment. Aquí és on una bona integració amb l'ant i l'ús d'una eina d'integració continua han de fer la seva feina.&lt;br /&gt;&lt;br /&gt;Cada cop estic més convençut que programar és més un art que cap altra cosa, però això no ens ha d'impedir conèixer les eines que ens poden ajudar a aconseguir la qualitat del nostre codi.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-6423869519834525183?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/6423869519834525183/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=6423869519834525183' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/6423869519834525183'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/6423869519834525183'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2008/03/quan-anam-programant-s-important-anar.html' title='Eines de control de qualitat'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-5376033863797805880</id><published>2008-03-01T12:10:00.000+01:00</published><updated>2008-03-01T12:41:44.670+01:00</updated><title type='text'>Comencen els cursos</title><content type='html'>Comença la temporada de cursos.&lt;br /&gt;&lt;br /&gt;Dilluns s'inicia el de Java bàsic, organitzat per la &lt;a href="http://caeb.es/"&gt;Caeb &lt;/a&gt;i  &lt;a href="http://www.grupsoftbalear.com/"&gt;GS Bit&lt;/a&gt;, de 30 hores. Enguany hem canviat la configuració, fent les sessions diàries de 3 hores (en lloc de les 4 de l'any passat) i ampliant la durada del curs de 20 a 30 hores.&lt;br /&gt;&lt;br /&gt;Crec que això afavorirà l'aprofitament del curs al temps que ho farà menys dur. S'ha de tenir en compte que tots venim de treballar al matí i el cansament és un factor a tenir en compte.&lt;br /&gt;&lt;br /&gt;L'època de cursos suposa un gran esforç. A la feina del matí (que no és poca!), s'ha d'afegir la tasca de preparar els cursos i la tensió pròpia d'explicar a unes 15 persones material bastant avançat. Sí, inclús el "Java &lt;span style="font-style: italic;"&gt;bàsic&lt;/span&gt;" requereix d'aspectes avançats si vols que els alumnes aprofitin bé el curs. Ara bé, amb aquest esforç també ve una gran motivació. Soc un entusiasta de les tecnologies que formen part dels cursos del catàleg (ejem ... excepte els EJBs...) i m'agrada ajudar als companys de professió a introduir-se en aquestes.&lt;br /&gt;&lt;br /&gt;La satisfacció que et dona veure l'"ajà!" d'algú que interioritza algun concepte complexe que a tu et va dur setmanes entendre o notar l'entusiasme mostrat cap a alguna eina de les que presentem no la donen altres tasques de la nostra professió.&lt;br /&gt;&lt;br /&gt;Au idò! a menjar bé, dormir prest, reduir les dosis de cafè i endevant amb els cursos!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-5376033863797805880?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/5376033863797805880/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=5376033863797805880' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/5376033863797805880'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/5376033863797805880'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2008/03/comencen-els-cursos.html' title='Comencen els cursos'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1706575371325125886.post-6756491963305290566</id><published>2007-12-02T02:38:00.000+01:00</published><updated>2008-03-01T11:50:26.368+01:00</updated><title type='text'>Lògica</title><content type='html'>Quan anem construint una aplicació, emprem distintes tècniques per resoldre diferents aspectes del problema. Dissenyem pantalles que organitzin les dades que l'usuari ha de introduir o consultar, creem les taules amb les dades que s'han de guardar més enllà de l'execució d'una operació concreta, etc.&lt;br /&gt;&lt;br /&gt;Però que fem quan hem d'implementar una determinada operació complexa?&lt;br /&gt;Un exemple: suposem que tenim una matrícula en un centre docent. Aquesta matrícula pot tenir distints periodes de baixa, amb una data d'inici, una de fi, i un motiu. El fet que la matrícula estigui activa pot dependre de moltes coses, una d'elles, que no hi hagi cap motiu de baixa actiu.&lt;br /&gt;&lt;br /&gt;Sovint veig plantejar aquest problema amb terme de modificacions en la base de dades. Un mètode de la capa de servei modifica les dades dels periodes de baixa, i segons el resultat final, l'estat de la matrícula queda actiu o no.&lt;br /&gt;&lt;br /&gt;La classe Matricula no incorpora cap lògica, només serveix per tenir en memòria una imatge de la taula de la base de dades amb la que treballar.&lt;br /&gt;&lt;br /&gt;Si volem fer ús de l'orientació a objectes, hem de retornar a la matricula la responsabilitat que li correspon, en aquest cas determinar quan està o no activa. Si les modificacions als periodes de baixa permetem fer-los només mitjançant la classe Matricula i fem que aquestes incorporin les regles de negoci que determinen la vigència d'una matrícula estarem obtinguent molts de beneficis:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;La lògica queda deslligada d'altres aspectes com la persistència, que segurament estava implícita si feiem la implementació als mètodes de servei&lt;/li&gt;&lt;li&gt;Se reduiex la complexitat al encapçular via la matrícula tots els accesos als periodes de baixa&lt;/li&gt;&lt;li&gt;Tenim un punt a on estendre la lògica associada a la validesa de la matrícula, en cas que sigui necessari&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Estem molt més ben equipats per reduir la duplicitat de codi&lt;/li&gt;&lt;li&gt;El codi queda més fàcil de provar&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1706575371325125886-6756491963305290566?l=domingosebastian.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://domingosebastian.blogspot.com/feeds/6756491963305290566/comments/default' title='Comentaris del missatge'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1706575371325125886&amp;postID=6756491963305290566' title='0 comentaris'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/6756491963305290566'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1706575371325125886/posts/default/6756491963305290566'/><link rel='alternate' type='text/html' href='http://domingosebastian.blogspot.com/2007/12/lgica.html' title='Lògica'/><author><name>Domingo Sebastian Sastre</name><uri>https://profiles.google.com/101052888850241299114</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-NsH5ejw9OVc/AAAAAAAAAAI/AAAAAAAAAAA/AjdcY6eGWCY/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry></feed>
