« Septiembre 2004 | Inicio | Noviembre 2004 »

Octubre 29, 2004

Nunca entenderÈ a los USA

Lo siento, pero no puedo entender determinadas cosas.

La supuesta "primera democracia del mundo", el paÌs que cree firmemente que han inventado la democracia y la libertad, que se empeÒa en imponer su sistema a todo el mundo a sangre y fuego, es la vez un paÌs en el que se pierden votos emitidos por correo, y en el que se publican cosas como Èsta:

moveonelectionprotection.gif

Por si alguno no entiende bien el significado de ese folleto, contiene las instrucciones para denunciar cualquier posible interferencia en el proceso de votaciÛn en las prÛximas elecciones presidenciales.

No sÈ si alguna vez llegarÈ a entender cÛmo la misma gente que avasalla al resto del mundo con su fanatismo "democr·tico-libertario", puede asumir con tanta naturalidad que en sus elecciones presidenciales puede, y probablemente vaya a haber, intentos m·s que serios de pucherazo.

Hoy se firma la ConstituciÛn Europea en Roma. Por favor, que nuestros gobernantes hagan todo lo posible ahora y en el futuro para que podamos mantenernos independientes de "esa gran naciÛn". Que ya hemos pagado bastante por no serlo.

Octubre 27, 2004

[OT] John Peel ha muerto

John Peel era un DJ mÌtico de la BBC. Durante muchos aÒos, los discos titulados "The Peel Sessions" fueron objetos de culto. Era respetado por los m˙sicos de todo el mundo, que sabÌan que podÌan enviarle una maqueta, porque, como mÌnimo, la iba a escuchar con interÈs. Pero sobre todo, para mÌ siempre ha sido y ser· el sÌmbolo de una Època.

Hablo de hace ya 15-20 aÒos ( verlo escrito asusta ), cuando aquÌ casi sÛlo sabÌamos de la existencia de ciertos grupos por rumores, porque alguien habÌa conseguido coger la BBC por la noche en casa y habÌa oÌdo el programa de John Peel, o porque en alguna tienda en Madrid vendÌan ( de importaciÛn, claro, o sea cuatro copias y carÌsimas ) las Peel Sessions. Eran los comienzos de New Order, de The Jesus And Mary Chain, de Orbital, de The Wedding Present.....

No es que los tiempos pasados fueran mejores, pero hoy siento que, con la muerte de John Peel, se ha cerrado de golpe ese capÌtulo. Yo le echarÈ de menos.

Noticia en la BBC

Octubre 26, 2004

TelefÛnica y el 3G

øAdivinan?. Otra queja sobre telefÛnica. DespuÈs de dos dÌas de buscar en la web de movistar y de llamar al servicio de "atenciÛn" al cliente, sigo sin saber cu·les son las tarifas del UMTS de TelefÛnica. Impresionante.

Por cierto, el microsite sobre el UMTS/3G de Vodafone me ha gustado bastante. No es que sea gran cosa, pero me gusta ver que los microsites en Flash se van convirtiendo en algo normal, no sÛlo en un vehÌculo para que los profesionales nos vendamos. Y por cierto, ellos sÌ que dicen lo que cuesta ( aunque se callan el precio de las videoconferencias , perdÛn, videollamadas ).

Y cuando se me pase el cabreo con el Consejero de Transportes de la Comunidad de Madrid, tal vez hablemos del estupendo servicio que nos presta el Metro. DejÈmoslo por hoy.

Octubre 19, 2004

[software] Maxivista

MaxiVista puede convertir cualquier ordenador ( protatil, Tablet PC ) en un segundo monitor para tu m·quina de desarrollo. Las ventanas se pueden mover de un monitor a otro, como si los dos fueran parte del mismo. Simplemente, conect·ndolos a travÈs de red, e instalando este programa

He estado utilizando el programa durante la ˙ltima semana, y he quedado impresionado, la verdad.

InstalaciÛn

El proceso de instalaciÛn fue simple y r·pido. Doble click en el instalador, y tras unos momentos, el software est· instalado y listo para utilizar. Por cierto, hay una guÌa del proceso de instalaciÛn bastante detallada aquÌ

Primera ejecuciÛn

Tras introducir el n˙mero de serie, el programa me pregunta si quiero generar el ejecutable que se tendr· que copiar al ordenador que har· como segundo monitor. Tras generarlo y copiarlo a mi portatil, al ejecutarlo, el monitor de mi portatil comienza a mostrar la misma informaciÛn que el monitor de mi ordenador de sobremesa. °Bien!.

Por defecto, Maxivista espera que el monitor primario estÈ a la izquierda y el secundario a la derecha. Como en mi caso, la colocaciÛn es al revÈs, tuve que cambiar esa configuraciÛn. Ning˙n problema. La soluciÛn estaba en la ayuda online ( eso sÌ, recuerda cerrar tanto el programa en el ordenador primario como en el secundario antes de cambiar la configuraciÛn ).

Adem·s, el modo por defecto es "mirror", es decir, tanto el ordenador primario como el secundario muestran lo mismo, pero eso tambiÈn es muy f·cil de cambiar. Simplemente haz click con el botÛn derecho en el icono de Maxivista en la bandeja del sistema, y selecciona la opciÛn apropiada.

Rendimiento

No he encontrado problemas de rendimiento al correr el software. De hecho, el ˙nico "problema" lo he tenido al correr Firefox 0.8 en el monitor secundario ( los men˙s aparecen en el primario ). Funciona perfectamente con los programas que utilizo habitualmente ( Flash, Outlook, Total Commander, Windows Explorer, etc ).

En cuanto al uso de CPU, no ha llegado al 5% en el ordenador primario ( Pentium IV 1 GHz ), y en el ordenador secundario es despreciable.

Puedes ver unos videos promocionales de Maxivista en funcionamiento aquÌ y aquÌ.

Concluyendo, si tienes m·s de un ordenador, Èsta puede ser una soluciÛn barata para utilizar m·s de un monitor ( el precio es de 49.95 dÛlares ).

Puedes encontrar m·s informaciÛn en la homepage de Maxivista

Octubre 13, 2004

En caso de duda, composiciÛn ( y III )

Aparte de las consideraciones expuestas en el post anterior, hay un par de razones para favorecer la composiciÛn sobre la herencia que probablemente sean de mucho m·s peso.

En primer lugar, la herencia no permite cambiar las implementaciones de las clases heredadas en tiempo de ejecuciÛn, y adem·s, rompe la encapsulaciÛn, ya que la clase base es visible a travÈs de las clases heredadas.

Sin embargo, la composiciÛn de objetos se suele hacer din·micamente en tiempo de ejecuciÛn, por lo que requiere que todos los objetos tengan muy presentes las interfaces de los otros, lo cual nos obliga a tener mucho cuidado al implementar las interfaces de todos los objetos de nuestro sistema, lo cual, a su vez, favorece la encapsulaciÛn, ya que el ˙nico conocimiento que van a tener unos objetos de otros ser· el de la interfaz.

Pero adem·s, la composiciÛn de objetos tiene otra gran ventaja. Favorecer la composiciÛn frente a la herencia, hace que nuestros programas sean m·s una especie de puzzle formado por pequeÒas piezas ( objetos ), altamente< encapsuladas, y que se encargan cada una de una sola tarea. De esta manera, las jerarquÌas de clases no se convertir·n en monstruos inmanejables, y no sÛlo obtendremos esa ventaja, sino que al estar basado en la composiciÛn de objetos, y no en clases, el comportamiento del sistema podr· cambiar m·s f·cilmente en tiempo de ejecuciÛn, y no estar· determinado por el cÛdigo que hayamos escrito en una clase. Por tanto, ser· mucho m·s flexible y adaptable.

Volvemos a intentar explicarlo con un ejemplo. Supongamos que estamos trabajando en una aplicaciÛn de gestiÛn de, por ejemplo, un gimnasio. Una de las partes de la aplicaciÛn debe mostrar un listado de los socios del gimnasio, que se carga de un XML.

Bien, pues podemos implementar una clase que extienda de XML, y que se encargue de cargar ese archivo, parsearlo, y devuelva un array de objetos con los datos de todos los socios.

Pero, øquÈ pasa si el resto del programa cambia, y ya no necesitamos un array de objetos con esos datos, sino otra estructura distinta?. Bueno, pues podemos escribir otra clase, que extienda tambiÈn XML, y con un mÈtodo distinto de parseo, que devuelva la estructura de datos que necesitamos. O incluso, extender la clase que escribimos en un primer momento, y sobrescribir su mÈtodo de parseo. ø Y si ahora nos dicen que el parseo tiene que tener cierta lÛgica, porque el XML tiene cierta sem·ntica? ( con valores dependientes del valor de alg˙n atributo, por ejemplo ). øEscribimos otra clase nueva?.

ø Y si desde un primer momento atacamos el problema siguiendo el ejemplo del puzzle? øPorquÈ no hacemos una clase que simplemente cargue un XML y devuelva un objeto XMLNode con el contenido del firstChild, y dejamos que sea el objeto que necesite de esos datos el que se encargue de parsearlos y materializar al estructura en memoria que necesite?. øPorquÈ?. Entre otras razones, porque realizar ambas tareas ( carga y parseo ) implica que la clase encargada de la carga tenga conocimiento de cÛmo y para quÈ quiere los datos el modelo de la aplicaciÛn . No sÛlo ganamos en flexibilidad en este caso, sino que tenemos un componente ( la clase que carga un xml y devuelve un XMLNode ) que podremos utilizar en todos y cada uno de nuestros proyectos.. Aparte de otros beneficios como el desacoplar la estructura de datos del servicio a travÈs del cual se obtiene, encapsular los datos y la forma de obtenerlos, etc.

Visto asÌ, parece que la soluciÛn ideal es la composiciÛn, øno?. Bueno, tambiÈn tiene sus puntos oscuros. En primer lugar, hemos dicho que el basar nuestros sistemas en piezas que se componen en tiempo de ejecuciÛn favorece la flexibilidad y adaptabilidad de los mismos, pero eso hace tambiÈn que sean m·s propensos a que aparezcan bugs no previstos, y adem·s, que esos bugs sean m·s difÌciles de debugear que en un sistema con un comportamiento m·s restrictivo. Adem·s, el cÛdigo basado en composiciÛn puede ejecutarse de forma m·s lenta que el basado en herencia, lo cual es importante cuando hablamos de flash.

Por todo lo que he intentado explicar, considero que es conveniente favorecer la composiciÛn de objetos frente a la herencia de clases. Eso no quiere decir que no debamos heredar nunca. SÛlo que, en una situaciÛn ideal, lo ˙nico que tendrÌamos que hacer es ir creando nuevos componentes seg˙n los fuÈramos necesitando. Aunque, en realidad, siempre vamos a necesitar de la uniÛn de estos dos mecanismos.

Volviendo al ejemplo de la carga del XML. HabÌamos visto la posibilidad de escribir una clase que cargara el XML, y devolviera un XMLNode con su contenido. Parece lÛgico ( realmente, casi obligado ) hacer que esa clase extienda de XML. Igualmente, la clase encargada del parseo, que recordemos, no tiene porquÈ ser siempre la misma, ni parsear de la misma forma, deberÌa, o bien extender de una clase "abstracta" que implementara el mÈtodo de parseo, o dada la falta de clases abstractas en actionScript, implementar un interfaz en el que se definiera ese mÈtodo. Pero lo que no es lÛgico es que una sola clase tenga asignada ambas responsabilidades

Dicho de otra forma, la herencia no es mala. Muchas veces es incluso casi obligatoria, pero siempre que la utilicemos ( al igual que la composiciÛn ) para solucionar nuestros problemas, no como una imposiciÛn. Y la mayorÌa de las veces, nuestros problemas se van a resolver mejor componiendo que heredando.

En caso de duda, composiciÛn ( II )

øQuÈ pasa si nuestra jerarquÌa de clases es muy profunda o est· muy extendida?. Pues que corremos un riesgo muy grande de tener problemas si cambia el interfaz de la superclase.

Seguimos teniendo a nuestros programadores y nuestros camioneros levant·ndose por las maÒanas, yendo a trabajar, y volviendo a casa por las noches. Supongamos que en la clase base ( Persona ) hay un mÈtodo como Èste:

public funcion pagar( cant: Number ): Billete { return new Billete( cant ); }

De manera que, cuando cualquier persona tiene que pagar algo ( la comida, el autob˙s, etc ), lo hace entregando un billete de la cantidad que corresponda. Recordemos que tanto los programadores como los camioneros extienden de persona, por lo que ya habr·n heredado ese mÈtodo.

Por tanto, todas las clases que interact˙en con las personas ( programadores o camioneros ), tendr·n que estar preparados para poder recoger billetes como medio de pago.

Pero øquÈ ocurre si, por cualquier circunstancia ( necesidades del programa, Ûrdenes del cliente,Ö ) necesitamos cambiar la forma de pago?. Es decir, øquÈ pasarÌa si ahora, en vez de pagar con billetes, las personas deben pagar con monedas, o con tarjeta de crÈdito?. Pues cualquier cambio en el interfaz de la clase base, por pequeÒo que sea, se propagar· a las clases hijas, y puede dar lugar a efectos inesperados en cualquier parte de nuestro programa. Porque, por ejemplo, el autob˙s no se puede pagar con tarjeta de crÈdito ( aunque el restaurante sÌ ).

Por tanto, cualquier cambio, por pequeÒo que sea, en la interfaz de la clase base puede requerir que hagamos cambios en muchos otras partes de nuestra aplicaciÛn. Y eso hay que intentar evitarlo.

Veamos una forma de hacerlo ( como casi siempre, no la ˙nica ). Supongamos que delegamos la responsabilidad de realizar los pagos en otra clase, llamada PagosManager

class PagosManager { var saldo: Number; function PagosManager( ) { } public function init( saldoInicial: Number ) { this.saldo = saldoInicial; } public funcion pagar( cant: Number ): Billete { return new Billete( cant ); } }

Y tanto las clases Programador como Camionero podrÌan implementar los pagos de la siguiente forma:

class Programador extends Persona { var pagosManager: PagosManager = new PagosManager( ); function Programador( ) { this.pagosManager = new PagosManager( ); this.pagosManager.init( 1000 ); } public function pagar( ): Billete { return this.pagosManager.pagar( 100 ); } }

Y si necesit·ramos que el Programador pagara con tarjeta de crÈdito, el ˙nico cambio a realizar serÌa en la propia implementaciÛn de la clase Programador:

public function pagar( ): Tarjeta { return new Tarjeta( this.pagosManager.pagar( 100 ) ); }

El ejemplo no es demasiado bueno ( ni completo ), pero espero que pueda servir para entender el concepto que intento transmitir.

Igualmente, si hemos utilizado la herencia para reutilizar cÛdigo, nos podemos encontrar con el problema inverso: que necesitemos que alguna de las clases hijas cambie su perfil, para lo cual debamos cambiar el perfil de la clase base, con lo cual, inducimos cambios en el resto de clases hijas ( que pueden no ser bienvenidos por el compilador, o por el resto del programa ).

Ve·moslo con un ejemplo. Si la clase base ( Persona ), implementa el siguiente mÈtodo:

public function getDNI( ): String { return this.dni; }

Ese mÈtodo estar· presente en la implementaciÛn de Programador y Camionero. Pero øquÈ pasa si al programador le pide su jefe que le diga su DNI pero encriptado por un algoritmo md5? ( lo sÈ, el ejemplo es malo, pero los jefes son capaces de cualquier cosa ). Pues que, al ser ese mÈtodo parte de la implementaciÛn heredada de la clase base, si cambiamos la implementaciÛn de ese mÈtodo, cambiar· tambiÈn para el Camionero, que empezar· a devolver su DNI encriptado. Obviamente, se puede sobrescribir el mÈtodo sÛlo en la clase Programador, pero para eso no hacÌa falta heredarlo, øno?, porque entonces ya no es funcionalidad com˙n, es interfaz com˙n, y eso, como dice la palabra, se puede hacer que sea parte de un interfaz com˙n para ambas clases hijas.

Y el tema todavÌa da para otro post

En caso de duda, composiciÛn ( I )

La forma en la que todos nos adentramos en el mundo de la programaciÛn orientada a objetos suele ser parecida. Tendemos a depender en exceso de la herencia, a realizar jerarquÌas de clases muy cerradas y muy estrictas, a solucionar cualquier problema, por pequeÒo que sea, a base de crear clases hijas particularizadas para ese problema. øPero es Èsa la mejor forma de organizar nuestro cÛdigo?

Hace poco, alguien me dijo que discutir sobre herencia y composiciÛn era como discutir sobre pc vs mac. Yo creo que no es asÌ. Voy a intentar explicar por quÈ

La herencia es un mecanismo muy poderoso, probablemente sea la herramienta m·s poderosa que nos ofrece cualquier lenguaje orientado a objetos, y probablemente, por eso tambiÈn, la que es m·s susceptible de ser mal utilizada. Pero primero veamos quÈ es la herencia.

B·sicamente, la herencia permite definir una clase utilizando como base otra clase ya existente. Ve·moslo con un ejemplo claro ( probablemente, el ejemplo m·s utilizado de la historia ).

class Persona { function Persona( ) { } public function comer( ){} public function dormir( ){} public function hablar( ){} } class Programador extends Persona { function Programador( ){} } Class Camionero extends Persona { function Camionero( ){} }

Tanto el programador como el camionero ( en ambos casos, mientras no se demuestre lo contrario ) son personas, por lo que habr· una serie de comportamientos o acciones comunes a ambos ( comer, dormir, hablar ) que son las que les corresponden por el hecho de ser personas. Por tanto, aquÌ ya se nos han planteado los dos conceptos que van m·s unidos a la herencia.

Por un lado, la herencia permite la reutilizaciÛn de cÛdigo. Cuando haya varias clases que tengan que implementar una serie de mÈtodos comunes, y para evitar tener el mismo cÛdigo ( o cÛdigo muy parecido ) en muchos sitios, podemos simplemente implementar ese cÛdigo com˙n en una clase base, y hacer que el resto de clases hereden de Èsta.

Por otra parte, la herencia tambiÈn sirve para agrupar las clases conceptualmente. En este caso, tanto el programador como el camionero son personas, por lo tanto, es lÛgico que las clases que los representan hereden de la clase persona. De hecho, en todos los libros de programaciÛn orientada a objetos, Èsta es la norma que se da para saber cu·ndo se debe hacer que una clase herede de otra, la famosa "es-un". El programador "es una" persona, el camionero "es una" persona, por lo tanto deben heredar de la clase persona.

Adem·s, de esta manera, el polimorfismo es inmediato. Podemos utilizar una variable del tipo de la clase base para almacenar una instancia de cualquiera de sus subclases. En nuestro ejemplo, una variable de tipo Persona nos permitir· hacer referencia a un Programador, a un Camionero, etc.

La relaciÛn de composiciÛn, por el contrario, es una relaciÛn de pertenencia. Ve·moslo con un ejemplo.

class Programador extends Persona { var mascota: Mascota; function Programador( ) { this.mascota = new Mascota( ); } }

El programador tiene una mascota. …sa es la palabra clave para definir una relaciÛn de composiciÛn ( "tiene un" ).

Bien, hasta ahora todo parece estar bastante claro. Entonces, øporquÈ el tÌtulo del post es "en caso de duda, composiciÛn"?. øEs que puede haber duda ?.

Pues sÌ, hay dudas, y muchas, sobre si la herencia es realmente tan beneficiosa como parece. Veamos algunos casos en los que las implementaciones basadas en la herencia no son la mejor soluciÛn de entre todas las posibles.

En primer lugar, vamos a replantearnos el primer ejemplo. Ahora, vamos a implementar una clase "Trabajador", que es la que va a tener todas las caracterÌsticas propias de una persona que tiene un trabajo, y que, obviamente, heredar· de la clase Persona ( un trabajador "es una" Persona ).

class Trabajador extends Persona { function Trabajador( ){} public function entrarEnElTrabajo( ){} public function trabajar( ){} public function salirDelTrabajo( ){} }

Por lo tanto, ahora, las clases Programador y Camionero serÌan:

class Programador extends Trabajador { } class Camionero extends Trabajador { }

De este modo, tanto el Programador como el Camionero heredarÌan los mÈtodos de la clase Persona y los de la clase Trabajador.

Bien, tenemos una clase base ( Persona ) de la que extiende la clase Trabajador ( Un trabajador es una persona ), de la que a su vez extienden la clase Programador ( un Programador es un trabajador ) y Camionero ( que tambiÈn es un trabajador ). øCorrecto?. Bueno, pues no del todo. øPorquÈ?. Pues porque un programador no es sÛlo un trabajador. De hecho, algunos programadores, cuando salen de trabajar se convierten en novios, otros en amigos de alguien, otros en cinÈfilos, etc. Luego podemos decir que un programador no es siempre un trabajador.

Bien, pues como un programador no es siempre y en todo caso un trabajador, no deberÌa extender de la clase Trabajador, porque su comportamiento no va a ser siempre el de Trabajador. Dicho de otra forma, la prueba de "es un" para decidir si heredar, deberÌa refinarse m·s, y ser "es un y sÛlo un y siempre un".

En este caso, por ejemplo, la soluciÛn podrÌa pasar por que la funcionalidad correspondiente a los trabajadores formara parte de un interfaz que implementaran tanto el programador como el camionero. O, mejor a˙n, las clases Programador y Camionero pueden tener una variable de clase que sea una instancia de la clase Trabajador.

class Programador extends Persona { var workerPart: Trabajador = new Trabajador( ); public function entrarEnElTrabajo( ) { this.workerPart.entrarEnElTrabajo( ); } }

Esta soluciÛn nos trae otra ventaja. Si tuviÈramos que cambiar el perfil de alguno de los mÈtodos de la clase Trabajador, ese cambio sÛlo afectarÌa a las clases Programador y Camionero, ya que podrÌamos resolver ese cambio de perfil en ellas, por lo que el resto de clases que interact˙en con ellas no tienen porquÈ "notar" ese cambio. Hemos ganado tambiÈn en facilidad de refactorizaciÛn.

Pero no acaban ahÌ los problemas. Seguiremos en el prÛximo post.

Octubre 03, 2004

°Temblad, madrileÒos!

Por si ya Èramos pocos en Madrid, se cierne sobre nosotros una terrible amenaza. °Javier se viene a la capital!

No est· muy claro cÛmo, pero ha conseguido engaÒar a una empresa para que le dÈ trabajo. Seg˙n Èl mismo ha dicho, el trabajo parece interesante ( mucho remoting, bastante flashcomm ), de esos en los que hay que estar aprendiendo casi a diario.

TambiÈn parece que entre preparar mudanzas, embaucar a su hermano ( yo ) para que le ayude a buscar piso, y cerrar sus obligaciones en su trabajo actual ( que la verdad, abandona con cierta sensaciÛn de pesar ), no tiene tiempo ni para pensar en que no tiene tiempo.

°Enhorabuena! ( a Javier, claro, porque el resto de madrileÒos deberÌamos empezar a prepararnos para pasar una larga temporada en el refugio... )