« Marzo 2005 | Inicio | Mayo 2005 »

Abril 26, 2005

Planeta CÛdigo sigue creciendo

Planeta CÛdigo no es sÛlo un agregador sobre programaciÛn en castellano.

Es una comunidad con unos foros en los que se pueden encontrar recomendaciones de libros, discusiones sobre los IDEs, o los sistemas de control de versiones.

Pero ahora, adem·s, tambiÈn es un wiki, orientado, al igual que el resto de la comunidad, a tratar temas relacionados con el desarrollo de software ( buenas pr·cticas de programaciÛn, desarrollo ·gil, etc ).

°A disfrutarlo!

Un ejemplo del patron Command

Todo est· preparado. Las ovejas y las vacas han sido clonadas y sus roles han sido asignados. Es el momento perfecto para que el Profesor Dispar lance su ataque final. °°°°°Ha llegado el momento de conquistar el mundo!!!!

øPero cÛmo dar· el Profesor Dispar a sus tropas la orden de atacar?

Si recuerdas los posts anteriores, El Profesor Dispar ha conseguido clonar cualquier animal, y despuÈs, ha conseguido asignarles cualquier rol de forma din·mica. AsÌ pues, el Profesor ha clonado ovejas y vacas, y dispone de un ejÈrcito de ovejas soldado, ovejas campesinas, vacas soldado y vacas campesinas ( es que no es f·cil dominar el mundo )

El Profesor Dispar est· loco, pero no es idiota. Quiere conquistar el mundo, cierto, tiene un malvado plan para hacerlo, cierto, pero sabe que tener un buen plan ( aunque sea malvado ) no es una garantÌa para el Èxito. Necesita un plan de emergencia.

El Profesor quiere que algunas de sus ovjeas soldado participen en el primer ( y por tanto, glorioso ) ataque. Pero tambiÈn quiere reservar unas cuantas de sus ovejas soldado y dejarlas descansando mientras sus colegas son masacradas en el campo de batalla ( perdÛn por el exceso de violencia, pero, ya se sabe, dominar el mundo es algo para lo que hace falta estÛmago ), y que puedan ser utilizadas como refuerzos.

Pero, øcÛmo podr· hacerlo?. El Profesor es un hombre muuuuuy ocupado, por lo tanto el ataque debe poderse lanzar sin mucha intervenciÛn por su parte. Algo tan simple como apretar el botÛn de "dominar el mundo" serÌa perfecto. Es r·pido, es f·cil, y el Profesor puede delegar la tarea en alguno de sus subordinados ( muhahahahahahahahaaa ). Eso serÌa perfecto, pero para conseguirlo tiene que encontrar la forma de poderle decir a cada oveja que es lo que se supone que debe hacer cuando el "glorioso momento del ataque" llegue.

øPero cÛmo ?. El conocimiento que tiene el Profesor de las ovjeas soldado es su interfaz. El Profesor sabe que cada oveja soldado implementa el interfaz ISoldierActions ( por favor, si no lo has leÌdo, revisa el post sobre el patrÛn extension objects, porque voy a utilizar parte del cÛdigo de ese post ). Resumiendo, lo que en realidad necesita es que algunas ovejas ejecuten uno de los mÈtodos de ISoldierActions, y otras ovejas ejecuten uno distinto cuando reciban la orden de atacar.

Intentemos entenderlo con un ejemplo. AquÌ est·n el interfaz ISoldierActions, y la clase SoldierRole ( un poco diferentes de las que utilizamos en el post del extension objects):

interface ISoldierActions { public function destroy( ); public function moveTo( ); public function waitForMoreOrders( ); } class SoldierRole extends Role implements ISoldierActions { private var subject: IBasicActions; function SoldierRole( subject: IBasicActions ) { this.subject = subject; trace( "SoliderBehaviour created" ); } public function destroy( ) { //Specific behaviour trace( "Soldier interface. destroy" ); //Uses some of the animal's methods subject.eat( ); } public function moveTo( ) { //Specific behaviour trace( "Soldier Interface. moveTo" ); //Uses some of the animal's methods subject.moveLegs( ); } public function waitForMoreOrders( ) { trace( "Beeeee, I'll wait for more orders" ); } }

Tanto el interfaz IPeasantActions como la clase PeasantRole no han cambiado

El Profesor Dispar ha clonado diez mil ovejas soldado, y diez mil ovejas campesinas, y utiliza dos arrays para almacenar una referencia a todas ellas.

Por tanto, al apretar el botÛn de "al ataqueeeeeeeeeeeeeee" podrÌa hacer algo asÌ:

class ProfesorDispar { public static function attack( soldiers: Array, peasants: Array ) { var numSoldiers: Number = soldiers.length; var numPeasants: Number = peasants.length; for( var i=0; i< numSoldiers / 2; i++ ) { soldiers[ i ].destroy( ); } for( var i=numSoldiers/2; i< numSoldiers; i++ ) { soldiers[ i ].waitForMoreOrders( ); } for( var i=0; i< numPeasants / 2; i++ ) { peasants[ i ].doGardening( ); } for( var i=numPeasants/2; i< numPeasants; i++ ) { peasants[ i ].driveTo( ); } } }

Y en la lÌnea de tiempo principal:

var soldiers: Array = new Array( ); var peasants: Array = new Array( ); for( var i=0; i<10; i++ ) { var sheep: Sheep = new Sheep( ); sheep.addExtension( "SoldierRole", new SoldierRole( sheep ) ); var soldierSheep: ISoldierActions = ISoldierActions( sheep.getExtension( "SoldierRole" ) ); soldiers.push( soldierSheep ); } for( var i=10; i<20; i++ ) { var sheep: Sheep = new Sheep( ); sheep.addExtension( "PeasantRole", new PeasantRole( sheep ) ); var peasantSheep: IPeasantActions = IPeasantActions( sheep.getExtension( "PeasantRole" ) ); peasants.push( peasantSheep ); } ProfesorDispar.attack( soldiers, peasants );

( InsÈrtense varios minutos de risas histÈricas aquÌ, por favor ). Ya sabes lo que viene ahora, n o?. Exacto. El Profesor Dispar est· loco, pero no es un idiota. No le gusta la soluciÛn que ha encontrado. øPor quÈ?. Bueno, no es exactamente lo que querÌa. El Profesor simplemente quiere poder decir "al ataqueeeeeeeee", y comenzar a reirse histÈricamente mientras las ovejas se lanzan ciegamente a cumplir sus Ûrdenes.

El Profesor barrunta que todo podrÌa ser mucho m·s f·cil si pudiera darle a cada una de las ovejas un sobre conteniendo sus Ûrdenes. Cuando el "momento de gloria" llegue, simplemente le tendr· que decir a cada oveja que habra el sobre y obedezca las Ûrdenes contenidas en Èl. Pero no quiere saber de quÈ forma tienen que cumplir esas Ûrdenes las ovejas, y de hecho, ni siquiera quiere saber si le est· mandando algo a una oveja o a una vaca, o a lo que sea.

oveja_sobre.jpg

Pero justo en ese momento, el Profesor recuerda sus aÒos mozos, cuando era estudiante, y entre partida y partida de tute leyÛ el GoF. Y entonces, comienza a reÌrse con m·s fuerza que nunca. Porque ha recordado el patrÛn Command.

El Profesor quiere dar cuatro Ûrdenes diferentes. Dos para ser obedecidas por las ovejas soldado ( "ataca", y "espera hasta recibir nuevas Ûrdenes" ), y las otras dos dirigidas a las ovejas campesinas ( "comience a trabajar en el jardÌn", y "lleva el tractor a casa" ).

Por tanto, va a encapsular la orden, y el receptor de dicha orden, en un paquete ( el sobre ). øCÛmo?. Mira:

En primer lugar, escribir· el siguiente interfaz:

interface ICommandActions { public function execute( ); }

Y los distintos comandos ser·n:

class SoldierAttack implements ICommandActions { private var receiver: ISoldierActions; public function SoldierAttack( soldier: ISoldierActions ) { this.receiver = soldier; } public function execute( ) { this.receiver.destroy( ); } } class SoldierWait implements ICommandActions { private var receiver: ISoldierActions; public function SoldierWait( soldier: ISoldierActions ) { this.receiver = soldier; } public function execute( ) { this.receiver.waitForMoreOrders( ); } } class PeasantAttack implements ICommandActions { private var receiver: IPeasantActions; function PeasantAttack( peasant: IPeasantActions ) { this.receiver = peasant; } public function execute( ) { receiver.doGardening( ); } } class PeasantDrive implements ICommandActions { private var receiver: IPeasantActions; function PeasantDrive( peasant: IPeasantActions ) { this.receiver = peasant; } public function execute( ) { receiver.driveTo( ); } }

Por tanto, cuando el Profesor presione el botÛn de "al ataqueeeeeeeeee", tendr· que hacer algo asÌ:

class ProfesorDispar { public static function attack( commandsArray: Array ) { for( var k=0; k< commandsArray.length; k++ ) { commandsArray[ k ].execute( ); } } }

Ese mÈtodo recibe como par·metro un array conteniendo todos los comandos. Ese array se podrÌa construir con cÛdigo similar a Èste:

var theArmy: Array = new Array( ); for( var i=0; i<5; i++ ) { var sheep: Sheep = new Sheep( ); sheep.addExtension( "SoldierRole", new SoldierRole( sheep ) ); var soldierSheep: ISoldierActions = ISoldierActions( sheep.getExtension( "SoldierRole" ) ); var saCommand: SoldierAttack = new SoldierAttack( soldierSheep ); theArmy.push( saCommand ); } for( var i=5; i<10; i++ ) { var sheep: Sheep = new Sheep( ); sheep.addExtension( "SoldierRole", new SoldierRole( sheep ) ); var soldierSheep: ISoldierActions = ISoldierActions( sheep.getExtension( "SoldierRole" ) ); var swCommand: SoldierWait = new SoldierWait( soldierSheep ); theArmy.push( swCommand ); } for( var i=10; i<15; i++ ) { var sheep: Sheep = new Sheep( ); sheep.addExtension( "PeasantRole", new PeasantRole( sheep ) ); var peasantSheep: IPeasantActions = IPeasantActions( sheep.getExtension( "PeasantRole" ) ); var paCommand: PeasantAttack = new PeasantAttack( peasantSheep ); theArmy.push( paCommand ); } for( var i=15; i<20; i++ ) { var sheep: Sheep = new Sheep( ); sheep.addExtension( "PeasantRole", new PeasantRole( sheep ) ); var peasantSheep: IPeasantActions = IPeasantActions( sheep.getExtension( "PeasantRole" ) ); var pdCommand: PeasantDrive = new PeasantDrive( peasantSheep ); theArmy.push( pdCommand ); } ProfesorDispar.attack( theArmy );

El cÛdigo es horrible, pero sirve para ilustrar cÛmo el Profesor ha creado una colecciÛn de objetos, cada uno de los cuales encapsula un comando y el recpetor de dicho comando. Ahora, el Profesor Dispar no necesita saber nada acerca de esos comandos, ni del receptor de los mismo. Simplemente tiene que decir "eh, comando, ejec˙tese", y el comando har· el resto.

De hecho, el Profesor Dispar ha conseguido desacoplar tres procesos diferentes: la creaciÛn de los objetos ( implementando el patrÛn prototype ), la asignaciÛn de roles a esos objetos ( con el patrÛn extension objects ), y la forma en que esos roles pasan a la acciÛn ( el patrÛn command )

Al final va a resultar que sÌ es un genio...

Un ejemplo del patron Command ( la versiÛn Java )

Todo est· preparado. Las ovejas y las vacas han sido clonadas y sus roles han sido asignados. Es el momento perfecto para que el Profesor Dispar lance su ataque final. °°°°°Ha llegado el momento de conquistar el mundo!!!!

øPero cÛmo dar· el Profesor Dispar a sus tropas la orden de atacar?

Si recuerdas los posts anteriores, El Profesor Dispar ha conseguido clonar cualquier animal, y despuÈs, ha conseguido asignarles cualquier rol de forma din·mica. AsÌ pues, el Profesor ha clonado ovejas y vacas, y dispone de un ejÈrcito de ovejas soldado, ovejas campesinas, vacas soldado y vacas campesinas ( es que no es f·cil dominar el mundo )

El Profesor Dispar est· loco, pero no es idiota. Quiere conquistar el mundo, cierto, tiene un malvado plan para hacerlo, cierto, pero sabe que tener un buen plan ( aunque sea malvado ) no es una garantÌa para el Èxito. Necesita un plan de emergencia.

El Profesor quiere que algunas de sus ovjeas soldado participen en el primer ( y por tanto, glorioso ) ataque. Pero tambiÈn quiere reservar unas cuantas de sus ovejas soldado y dejarlas descansando mientras sus colegas son masacradas en el campo de batalla ( perdÛn por el exceso de violencia, pero, ya se sabe, dominar el mundo es algo para lo que hace falta estÛmago ), y que puedan ser utilizadas como refuerzos.

Pero, øcÛmo podr· hacerlo?. El Profesor es un hombre muuuuuy ocupado, por lo tanto el ataque debe poder ser lanzado sin mucha intervenviÛn por su parte. Algo tan simple como apretar el botÛn de "dominar el mundo" serÌa perfecto. Es r·pido, es f·cil, y el Profesor puede delegar la tarea en alguno de sus subordinados ( muhahahahahahahahaaa ). Eso serÌa perfecto, pero para conseguirlo tiene que encontrar la forma de poderle decir a cada oveja que es lo que se supone que debe hacer cuando el "glorioso momento del ataque" llegue.

øPero cÛmo ?. El conocimiento que tiene el Profesor de las ovjeas soldado es su interfaz. El Profesor sabe que cada oveja soldado implementa el interfaz ISoldierActions ( por favor, si no lo has leÌdo, revisa el post sobre el patrÛn extension objects, porque voy a utilizar parte del cÛdigo de ese post ). Resumiendo, lo que en realidad necesita es que algunas ovejas ejecuten uno de los mÈtodos de ISoldierActions, y otras ovejas ejecuten uno distinto cuando reciban la orden de atacar.

Intentemos entenderlo con un ejemplo. AquÌ est·n el interfaz ISoldierActions, y la clase SoldierRole ( un poco diferentes de las que utilizamos en el post del extension objects):

public interface ISoldierActions { public void destroy( ); public void moveTo( ); public void waitForMoreOrders( ); } public class SoldierRole extends Role implements ISoldierActions { private IBasicActions subject; public SoldierRole( IBasicActions subject ) { this.subject = subject; System.out.println( "SoliderBehaviour created" ); } public void destroy( ) { //Specific behaviour System.out.println( "Soldier interface. destroy" ); //Use some of the animal's methods subject.eat( ); } public void moveTo( ) { //Specific behaviour System.out.println( "Soldier Interface. moveTo" ); //Use some of the animal's methods subject.moveLegs( ); } public void waitForMoreOrders( ) { System.out.println( "I'll wait for more orders. Beeeeeee" ); } }

Tanto el interfaz IPeasantActions como la clase PeasantRole no han cambiado

El Profesor Dispar ha clonado diez mil ovejas soldado, y diez mil ovejas campesinas, y utiliza dos arrays para almacenar una referencia a todas ellas.

Por tanto, al apretar el botÛn de "al ataqueeeeeeeeeeeeeee" podrÌa hacer algo asÌ:

class ProfesorDispar { public ProfesorDispar( ) { } public void attack( ISoldierActions[] soldiers, IPeasantActions[] peasants ) { int soldiersCount = soldiers.length; int peasantsCount = peasants.length; for( int idx=0; idx< soldiersCount / 2; idx++ ) { soldiers[ idx ].destroy( ); } for( int idx=soldiersCount/2; idx< soldiersCount; idx++ ) { soldiers[ idx ].waitForMoreOrders( ); } for( int idx=0; idx< peasantsCount / 2; idx++ ) { peasants[ idx ].doGardening( ); } for( int idx=peasantsCount/2; idx< peasantsCount; idx++ ) { peasants[ idx ].driveTo( ); } } }

( InsÈrtense varios minutos de risas histÈricas aquÌ, por favor ). Ya sabes lo que viene ahora, n o?. Exacto. El Profesor Dispar est· loco, pero no es un idiota. No le gusta la soluciÛn que ha encontrado. øPor quÈ?. Bueno, no es exactamente lo que querÌa. El Profesor simplemente quiere poder decir "al ataqueeeeeeeee", y comenzar a reirse histÈricamente mientras las ovejas se lanzan ciegamente a cumplir sus Ûrdenes.

El Profesor barrunta que todo podrÌa ser mucho m·s f·cil si pudiera darle a cada una de las ovejas un sobre conteniendo sus Ûrdenes. Cuando el "momento de gloria" llegue, simplemente le tendr· que decir a cada oveja que habra el sobre y obedezca las Ûrdenes contenidas en Èl. Pero no quiere saber de quÈ forma tienen que cumplir esas Ûrdenes las ovejas, y de hecho, ni siquiera quiere saber si le est· mandando algo a una oveja o a una vaca, o a lo que sea.

oveja_sobre.jpg

Pero justo en ese momento, el Profesor recuerda sus aÒos mozos, cuando era estudiante, y entre partida y partida de tute leyÛ el GoF. Y entonces, comienza a reÌrse con m·s fuerza que nunca. Porque ha recordado el patrÛn Command.

El Profesor quiere dar cuatro Ûrdenes diferentes. Dos para ser obedecidas por las ovejas soldado ( "ataca", y "espera hasta recibir nuevas Ûrdenes" ), y las otras dos dirigidas a las ovejas campesinas ( "comience a trabajar en el jardÌn", y "lleva el tractor a casa" ).

Por tanto, va a encapsular la orden, y el receptor de dicha orden, en un paquete ( el sobre ). øCÛmo?. Mira:

En primer lugar, escribir· el siguiente interfaz:

public interface ICommandActions { public void execute( ); }

Y los distintos comandos ser·n:

public class SoldierAttack implements ICommandActions { ISoldierActions receiver; public SoldierAttack( ISoldierActions soldier ) { receiver = soldier; } public void execute( ) { receiver.destroy( ); } } public class SoldierWait implements ICommandActions { ISoldierActions receiver; public SoldierWait( ISoldierActions soldier ) { receiver = soldier; } public void execute( ) { receiver.waitForMoreOrders( ); } } public class PeasantAttack implements ICommandActions { IPeasantActions receiver; public PeasantAttack( IPeasantActions peasant ) { receiver = peasant; } public void execute( ) { receiver.doGardening( ); } } public class PeasantDrive implements ICommandActions { private IPeasantActions receiver; public PeasantDrive( IPeasantActions peasant ) { receiver = peasant; } public void execute( ) { receiver.driveTo( ); } }

Por tanto, cuando el Profesor presione el botÛn de "al ataqueeeeeeeeee", tendr· que hacer algo asÌ:

public void attack( ICommandActions[] theArmy ) { int sheepsCount = theArmy.length; for( int idx=0; idx< sheepsCount; idx++ ) { theArmy[ idx ].execute( ); } }

Ese mÈtodo recibe como par·metro un array conteniendo todos los comandos. Ese array se podrÌa construir con cÛdigo similar a Èste:

public class ProfesorDispar { public ProfesorDispar( ) { } public void attack( ICommandActions[] theArmy ) { int sheepsCount = theArmy.length; for( int idx=0; idx< sheepsCount; idx++ ) { theArmy[ idx ].execute( ); } } public static void main( String[ ] args ) { Sheep sheep = null; ICommandActions[ ] theArmy = new ICommandActions[ 20 ]; ISoldierActions soldierSheep = null; IPeasantActions peasantSheep = null; SoldierAttack saCommand = null; SoldierWait swCommand = null; PeasantAttack paCommand = null; PeasantDrive pdCommand = null; for( int i=0; i<5; i++ ) { sheep = new Sheep( ); sheep.addExtension( "SoldierRole", new SoldierRole( sheep ) ); soldierSheep = ( ISoldierActions ) sheep.getExtension( "SoldierRole" ); saCommand = new SoldierAttack( soldierSheep ); theArmy[ i ] = saCommand; } for( int i=5; i<10; i++ ) { sheep = new Sheep( ); sheep.addExtension( "SoldierRole", new SoldierRole( sheep ) ); soldierSheep = ( ISoldierActions ) sheep.getExtension( "SoldierRole" ); swCommand = new SoldierWait( soldierSheep ); theArmy[ i ] = swCommand; } for( int i=10; i<15; i++ ) { sheep = new Sheep( ); sheep.addExtension( "PeasantRole", new PeasantRole( sheep ) ); peasantSheep = ( IPeasantActions ) sheep.getExtension( "PeasantRole" ); paCommand = new PeasantAttack( peasantSheep ); theArmy[ i ] = paCommand; } for( int i=15; i<20; i++ ) { sheep = new Sheep( ); sheep.addExtension( "PeasantRole", new PeasantRole( sheep ) ); peasantSheep = ( IPeasantActions ) sheep.getExtension( "PeasantRole" ); pdCommand = new PeasantDrive( peasantSheep ); theArmy[ i ] = pdCommand; } ProfesorDispar profesor = new ProfesorDispar( ); profesor.attack( theArmy ); } }

El cÛdigo es horrible, pero sirve para ilustrar cÛmo el Profesor ha creado una colecciÛn de objetos, cada uno de los cuales encapsula un comando y el recpetor de dicho comando. Ahora, el Profesor Dispar no necesita saber nada acerca de esos comandos, ni del receptor de los mismo. Simplemente tiene que decir "eh, comando, ejec˙tese", y el comando har· el resto.

De hecho, el Profesor Dispar ha conseguido desacoplar tres procesos diferentes: la creaciÛn de los objetos ( implementando el patrÛn prototype ), la asignaciÛn de roles a esos objetos ( con el patrÛn extension objects ), y la forma en que esos roles pasan a la acciÛn ( el patrÛn command )

Al final va a resultar que sÌ es un genio...

Abril 06, 2005

Un ejemplo del patrÛn Extension Objects ( la versiÛn Java )

øRecuerdas al Professor Dispar?. øRecuerdas sus malvados planes para dominar el mundo?.

Hoy veremos cÛmo el patrÛn Extension Objects ( o "cÛmo cambiar el interfaz que implementa una clase en tiempo de ejecuciÛn" ) ha ayudado al Profesor Dispar. Pero no va a ser una tarea f·cil, porque este patrÛn es muy complejo, pero øquiÈn dijo que ser un genio del mal fuera f·cil?.


En el anterior episodio, el Profesor Dispar diseÒÛ una m·quina para clonar que era capaz de clonar cualquier animal, sin conocer su raza, credo o religiÛn, delegando los detalles del proceso de clonaciÛn en el propio animal.

Pues aquÌ empieza el episodio de hoy. El Profesor Dispar ha clonado ya nueve diez mil ovejas y nueve mil vacas ( dijo algo asÌ como "Èse el n˙mero exacto que necesito, juajuajuajajajajajaaaaaaa" ). Pero de repento, se diÛ cuenta que sÌ, va a necesitar muchas ovejas, y sÌ, va a necesitar muchas vacas, pero no todas las ovejas ( ni todas las vacas ) deber·n tener el mismo rol. øPor quÈ?. Bueno, dominar el mundo no es f·cil. Hace falta organizaciÛn. °Hace falta un ( malvado ) plan!.

Y el Profesor Dispar tiene un plan. Quiere entrenar a algunas de sus ovejas y de sus vacas como soldados. Quiere que sean m·quinas de matar inmisericordes ( perdÛn por la violencia, pero es lo que tiene ser un genio loco ). Pero tambiÈn quiere que algunas de las ovejas y algunas de las vacas trabajen en las f·bricas haciendo armas. Y tambiÈn necesitan que algunas ovejas y algunas vacas trabajen como granjeras, cultivando los alimentos de sus compaÒeras militares ( recuerda, el Profesor Dispar est· loco, pero no es idiota ). Por tanto, se puede decir que lo que necesita es poder asignar distintos roles a los clones que ha creado.

soldierSheep.jpg
Una oveja soldado. Sin comentarios.

[Nota]. Con el fin de mantener este tinglado un poco organizado, con un ˙nico patrÛn por post, escribirÈ las clases Sheep y Cow desde el principio. Pero el Profesor Dispar no lo har· asÌ. …l aÒadir· el cÛdigo del episodio de hoy al que ya tenÌa.

Bien, pues el genio del mal empieza a pensar. "Muy bien, tengo una clase que representa una oveja ( Sheep ). Y ahora necesito tener una oveja que se comporte como un soldado, y otra que se comporte como una campesina, y lo mismo con las vacas". Eso suena a "extender la funcionalidad de una clase", øno?. °Brillante!! "EscribirÈ una clase SoldierSheep ( oveja soldado ) que extienda a Sheep, y otra clase, PeasantSheep ( oveja campesina ), que tambiÈn extienda a Sheep". ( InsÈrtense cinco minutos de risas histÈricas en este punto, por favor ).

Pero antes de ponerse a escribir cÛdigo, el Profesor Dispar se da cuenta que probablemente Èsa no sea la mejor soluciÛn, porque cada vez que quiera asignar un nuevo rol a una oveja, tendr· que escribir una nueva subclase ( por ejemplo, una OvejaIngeniera, o una OvejaMaitre... ). Y adem·s, Èl no sabe a priori cu·ntos roles va a necesitar asignar a una oveja. Todo depende de lo que necesite en el futuro. Necesita dar soporte a la adiciÛn de interfaces desconocidos a la clase Sheep ( øno es un genio? )

TambiÈn se da cuenta que su primera forma de atacar el problema tiene otro punto dÈbil, y es que no podr· re-asignar un rol. Si crea una OvejaSoldado, tendr· una OvejaSoldado para siempre, aunque la necesite para cultivar cebollas ( recuerda, el Profesor est· loco, a veces tiene unas ideas muy extraÒas )

Pero tambiÈn se da cuenta de otro punto dÈbil, Èste mucho m·s sutil. Una OvejaSoldado y una Oveja son exactamente lo mismo. La ˙nica diferencia es que una de ellas tiene un comportamiento particular, pero en esencia, son lo mismo. Por tanto, no es muy apropiado representarlas utilizando clases distintas.

El Profesor Dispar est· loco, pero no es idiota ( sÌ, ya sÈ, ya sÈ que ya lo sabes ). Por tanto, decide que ha encontrado demasiados puntos dÈbiles para su idea inicial, y que por tanto, es el momento de acudir a google en busca de una soluciÛn.

peasantSheep.jpg
Una oveja campesina. Lo creas o no, su hobby es la jardinerÌa.

Tras varias b˙squedas fallidas, recuerda un libro (Pattern Languages of Program Design, Volume 3, Addison-Wesley, 1998. ), en el que Erich Gamma ( sÌ, uno de los autores del GoF ) escribiÛ un capÌtulo sobre el patrÛn Extension Objects.

Este patrÛn intenta anticipar la extensiÛn del interfaz de un objeto en el futuro ( eso quiere decir: se pueden aÒadir interfaces a una clase en tiempo de ejecuciÛn ).

AsÌ que empieza a leer, y a reirse. Y cuanto m·s lee, m·s se rÌe.

La idea es muy sencilla. Una clase ( Sheep ) ser· capaz de cambiar el interfaz que implementa en tiempo de ejecuciÛn, seleccionando ese interfaz de entre una colecciÛn de objetos ( los Extension Objects ). Cada uno de esos objetos encapsular· uno de los roles ( SoldierSheep, PeasantSheep,... ) ( A estas alturas, el Profesor Dispar ya lleva casi veinte minutos de risas histÈricas!! )

Pero øcÛmo ser· capaz la clase Sheep de seleccionar el interfaz a implementar? ø Y la clase Cow?. Bien, ambas clases implementar·n el siguiente interfaz:

public interface ISubject { public Role getExtension( String extName ); }

La clase Sheep implementa un interfaz para encapsular las acciones que implementa por ser un animal ( comer, mover las piernas, los brazos... perdÛn: las patas, las patas... )

public interface IBasicActions { public void moveArms( ); public void moveLegs( ); public void eat( ); }

Por tanto, la clase Sheep ser·:

public class Sheep implements ISubject, IBasicActions { private SoldierRole soldierRole; private PeasantRole peasantRole; public Sheep( ) { System.out.println( "I'm a sheep" ); } public Role getExtension( String extName ) { Role returnValue = null; if( extName.equals( "SoldierRole" ) ) { if( soldierRole == null ) { returnValue = new SoldierRole( this ); } else { returnValue = soldierRole; } } if( extName.equals( "PeasantRole" ) ) { if( peasantRole == null ) { returnValue = new PeasantRole( this ); } else { returnValue = peasantRole; } } return returnValue; } public void moveArms( ) { // implementaciÛn del movimiento System.out.println( "la oveja mueve un brazo" ); } public void moveLegs( ) { System.out.println( "mueve una pierna" ); } public void eat( ) { System.out.println( "munch. munch" ); } }

FÌjate en cÛmo esa clase implementa el mÈtodo getExtension, que elige la clase que debe devolver de entre una colecciÛn de roles ( que son variables de la clase ). ø Y los roles ?

AquÌ est· el rol base ( no he implementado nada en Èl, pero cualquier funcionalidad com˙n a los roles deberÌa implementarse aquÌ )

public abstract class Role { }

Por tanto, el rol de soldado:

So, the SoldierRole:

public class SoldierRole extends Role implements ISoldierActions { private IBasicActions subject; public SoldierRole( IBasicActions subject ) { this.subject = subject; System.out.println( "SoliderBehaviour created" ); } public void destroy( ) { //Comportamiento especÌfico System.out.println( "Soldier interface. destroy" ); //Utiliza alguno de los mÈtodos del animal subject.eat( ); } public void moveTo( ) { //Comportamiento especÌfico System.out.println( "Soldier Interface. moveTo" ); //Utiliza alguno de los mÈtodos del animal subject.moveLegs( ); } }

Y el rol de campesino:

public class PeasantRole extends Role implements IPeasantActions { private IBasicActions subject; public PeasantRole( IBasicActions subject ) { this.subject = subject; System.out.println( "PeasantBehaviour creado" ); } public void driveTo( ) { //Comportamiento especÌfico System.out.println( "Ya voy" ); //Utiliza alguno de los mÈtodos del animal subject.moveArms( ); subject.moveLegs( ); } public void doGardening( ) { //Comportamiento especÌfico System.out.println( "OK, gardening" ); //Utiliza alguno de los mÈtodos del animal subject.moveArms( ); } }

Ambas clases ( SoldierRole y PeasantRole ) implementan, respectivamente, los interfaces ISoldierActions e IPeasantActions. Esos son los interfaces que parecer· que implementa la clase Sheep

Mira:

public class ProfessorCoupling { public static void main( String[ ] args ) { Sheep sheep = new Sheep( ); ISoldierActions soldierSheep = ( ISoldierActions ) sheep.getExtension( "SoldierRole" ); soldierSheep.destroy( ); IPeasantActions peasantSheep = ( IPeasantActions ) sheep.getExtension( "PeasantRole" ); peasantSheep.doGardening( ); } }

( InsÈrtense treinta minutos de risas histÈricas aquÌ ). Pero, un momento!!!. El Profesor Dispar se acaba de dar cuenta de un pequeÒo problema! ( bueno, en realidad no es un problemas, es una soluciÛn difÌcil )

La clase Sheep tiene conocimiento de sus extensiones ( los roles ). Esas extensiones se guardan en variables de clase, asÌ que no hay forma de aÒadir alg˙n rol, o modificar los ya existentes en tiempo de ejecuciÛn

Por tanto, el Profesor decide refactorizar la clase Sheep. Esta clase ya no guardar· sus posibles extensiones en variables de clase, sino que manejar· una colecciÛn ( un HashMap, un objeto ) con esos roles. De esta forma, ser· posible aÒadir o eliminar extensiones ( roles ) cuando se necesite ( no te creerÌas cÛmo se est· riendo ahora ).

El nuevo interfaz ISubject:

public interface ISubject { public Role getExtension( String extName ); public void addExtension( String extName, Role extension ); public void removeExtension( String extName ); }

Y la nueva clase Sheep ( no me gusta un pelo la forma en la que el Profesor ha implementado la colecciÛn, utilizando un objeto, pero bueno... )

import java.util.HashMap; public class Sheep implements ISubject, IBasicActions { private HashMap rolesCol; public Sheep( ) { System.out.println( "I'm a sheep" ); rolesCol = new HashMap( ); } public Role getExtension( String extName ) { return ( Role ) rolesCol.get( extName ); } public void addExtension( String extName, Role extension ) { rolesCol.put( extName, extension ); } public void removeExtension( String extName ) { rolesCol.remove( extName ); } public void moveArms( ) { System.out.println( "mueve un brazo" ); } public void moveLegs( ) { System.out.println( "la oveja mueve una pierna" ); } public void eat( ) { // implementaciÛn del sistema de alimentaciÛn System.out.println( "munch. munch" ); } }

Y el Profesor Dispar puede hacer algo como:

public class ProfessorCoupling { public static void main( String[ ] args ) { Sheep sheep = new Sheep( ); sheep.addExtension( "SoldierRole", new SoldierRole( sheep ) ); ISoldierActions soldierSheep = ( ISoldierActions ) sheep.getExtension( "SoldierRole" ); soldierSheep.destroy(); sheep.removeExtension( "SoldierRole" ); sheep.addExtension( "PeasantRole", new PeasantRole( sheep ) ); IPeasantActions peasantSheep = ( IPeasantActions ) sheep.getExtension( "PeasantRole" ); peasantSheep.doGardening( ); } }

Por tanto, los clones creados por la m·quina para clonar pueden cambiar su aspecto en tiempo de ejecuciÛn ( primero pueden ser soldados, luego campesinos, m·s tarde lo que al Profesor le dÈ la gana ). El profesor ha conseguido mantener la abstracciÛn Oveja limpia de operaciones especÌficas para un cliente. Eso tambiÈn lo podrÌa haber conseguido gracias a la herencia, definiendo las operaciones para los clientes en las subclases, pero eso habrÌa generado una jerarquÌ difÌcil de manejar

La siguiente aventura del Profesor Dispar ser· m·s f·cil, lo prometo. °Incluso los genios del mal necesitan vacaciones!

[Nota importante]. Auto crÌtica: El cÛdigo para recuperar el rol activo no pasarÌa ning˙n control de calidad. DeberÌa implementarse de otra forma, el uso de una cadena de texto como clave es bastante peligroso

Un ejemplo del patrÛn Extension Objects

øRecuerdas al Professor Dispar?. øRecuerdas sus malvados planes para dominar el mundo?.

Hoy veremos cÛmo el patrÛn Extension Objects ( o "cÛmo cambiar el interfaz que implementa una clase en tiempo de ejecuciÛn" ) ha ayudado al Profesor Dispar. Pero no va a ser una tarea f·cil, porque este patrÛn es muy complejo, pero øquiÈn dijo que ser un genio del mal fuera f·cil?.


En el anterior episodio, el Profesor Dispar diseÒÛ una m·quina para clonar que era capaz de clonar cualquier animal, sin conocer su raza, credo o religiÛn, delegando los detalles del proceso de clonaciÛn en el propio animal.

Pues aquÌ empieza el episodio de hoy. El Profesor Dispar ha clonado ya nueve diez mil ovejas y nueve mil vacas ( dijo algo asÌ como "Èse el n˙mero exacto que necesito, juajuajuajajajajajaaaaaaa" ). Pero de repento, se diÛ cuenta que sÌ, va a necesitar muchas ovejas, y sÌ, va a necesitar muchas vacas, pero no todas las ovejas ( ni todas las vacas ) deber·n tener el mismo rol. øPor quÈ?. Bueno, dominar el mundo no es f·cil. Hace falta organizaciÛn. °Hace falta un ( malvado ) plan!.

Y el Profesor Dispar tiene un plan. Quiere entrenar a algunas de sus ovejas y de sus vacas como soldados. Quiere que sean m·quinas de matar inmisericordes ( perdÛn por la violencia, pero es lo que tiene ser un genio loco ). Pero tambiÈn quiere que algunas de las ovejas y algunas de las vacas trabajen en las f·bricas haciendo armas. Y tambiÈn necesitan que algunas ovejas y algunas vacas trabajen como granjeras, cultivando los alimentos de sus compaÒeras militares ( recuerda, el Profesor Dispar est· loco, pero no es idiota ). Por tanto, se puede decir que lo que necesita es poder asignar distintos roles a los clones que ha creado.

soldierSheep.jpg
Una oveja soldado. Sin comentarios.

[Nota]. Con el fin de mantener este tinglado un poco organizado, con un ˙nico patrÛn por post, escribirÈ las clases Sheep y Cow desde el principio. Pero el Profesor Dispar no lo har· asÌ. …l aÒadir· el cÛdigo del episodio de hoy al que ya tenÌa.

Bien, pues el genio del mal empieza a pensar. "Muy bien, tengo una clase que representa una oveja ( Sheep ). Y ahora necesito tener una oveja que se comporte como un soldado, y otra que se comporte como una campesina, y lo mismo con las vacas". Eso suena a "extender la funcionalidad de una clase", øno?. °Brillante!! "EscribirÈ una clase SoldierSheep ( oveja soldado ) que extienda a Sheep, y otra clase, PeasantSheep ( oveja campesina ), que tambiÈn extienda a Sheep". ( InsÈrtense cinco minutos de risas histÈricas en este punto, por favor ).

Pero antes de ponerse a escribir cÛdigo, el Profesor Dispar se da cuenta que probablemente Èsa no sea la mejor soluciÛn, porque cada vez que quiera asignar un nuevo rol a una oveja, tendr· que escribir una nueva subclase ( por ejemplo, una OvejaIngeniera, o una OvejaMaitre... ). Y adem·s, Èl no sabe a priori cu·ntos roles va a necesitar asignar a una oveja. Todo depende de lo que necesite en el futuro. Necesita dar soporte a la adiciÛn de interfaces desconocidos a la clase Sheep ( øno es un genio? )

TambiÈn se da cuenta que su primera forma de atacar el problema tiene otro punto dÈbil, y es que no podr· re-asignar un rol. Si crea una OvejaSoldado, tendr· una OvejaSoldado para siempre, aunque la necesite para cultivar cebollas ( recuerda, el Profesor est· loco, a veces tiene unas ideas muy extraÒas )

Pero tambiÈn se da cuenta de otro punto dÈbil, Èste mucho m·s sutil. Una OvejaSoldado y una Oveja son exactamente lo mismo. La ˙nica diferencia es que una de ellas tiene un comportamiento particular, pero en esencia, son lo mismo. Por tanto, no es muy apropiado representarlas utilizando clases distintas.

El Profesor Dispar est· loco, pero no es idiota ( sÌ, ya sÈ, ya sÈ que ya lo sabes ). Por tanto, decide que ha encontrado demasiados puntos dÈbiles para su idea inicial, y que por tanto, es el momento de acudir a google en busca de una soluciÛn.

peasantSheep.jpg
Una oveja campesina. Lo creas o no, su hobby es la jardinerÌa.

Tras varias b˙squedas fallidas, recuerda un libro (Pattern Languages of Program Design, Volume 3, Addison-Wesley, 1998. ), en el que Erich Gamma ( sÌ, uno de los autores del GoF ) escribiÛ un capÌtulo sobre el patrÛn Extension Objects.

Este patrÛn intenta anticipar la extensiÛn del interfaz de un objeto en el futuro ( eso quiere decir: se pueden aÒadir interfaces a una clase en tiempo de ejecuciÛn ).

AsÌ que empieza a leer, y a reirse. Y cuanto m·s lee, m·s se rÌe.

La idea es muy sencilla. Una clase ( Sheep ) ser· capaz de cambiar el interfaz que implementa en tiempo de ejecuciÛn, seleccionando ese interfaz de entre una colecciÛn de objetos ( los Extension Objects ). Cada uno de esos objetos encapsular· uno de los roles ( SoldierSheep, PeasantSheep,... ) ( A estas alturas, el Profesor Dispar ya lleva casi veinte minutos de risas histÈricas!! )

Pero øcÛmo ser· capaz la clase Sheep de seleccionar el interfaz a implementar? ø Y la clase Cow?. Bien, ambas clases implementar·n el siguiente interfaz:

interface ISubject { public function getExtension( extName: String ): Role; }

La clase Sheep implementa un interfaz para encapsular las acciones que implementa por ser un animal ( comer, mover las piernas, los brazos... perdÛn: las patas, las patas... )

interface IBasicActions { public function moveArms( ); public function moveLegs( ); public function eat( ); }

Por tanto, la clase Sheep ser·:

class Sheep implements ISubject, IBasicActions { private var soldierRole: SoldierRole; private var peasantRole: PeasantRole; function Sheep( ) { trace( "Soy una oveja" ); } public function getExtension( extName: String ): Role { var returnValue: Role = null; if( extName == "SoldierRole" ) { if( soldierRole == null ) { returnValue = new SoldierRole( this ); } else { returnValue = soldierRole; } } if( extName == "PeasantRole" ) { if( peasantRole == null ) { returnValue = new PeasantRole( this ); } else { returnValue = peasantRole; } } return returnValue; } public function moveArms( ) { // implementaciÛn del movimiento trace( "la oveja mueve un brazo" ); } public function moveLegs( ) { // implementaciÛn del movimiento trace( "la oveja mueve una pierna" ); } public function eat( ) { // implementaciÛn del sistema de alimentaciÛn trace( "munch. munch" ); } }

FÌjate en cÛmo esa clase implementa el mÈtodo getExtension, que elige la clase que debe devolver de entre una colecciÛn de roles ( que son variables de la clase ). ø Y los roles ?

AquÌ est· el rol base ( no he implementado nada en Èl, pero cualquier funcionalidad com˙n a los roles deberÌa implementarse aquÌ )

class Role { function Role( ) { } }

Por tanto, el rol de soldado:

class SoldierRole extends Role implements ISoldierActions { private var subject: IBasicActions; function SoldierRole( subject: IBasicActions ) { this.subject = subject; trace( "Se crea el rol de soldado" ); } public function destroy( ) { //Comportamiento especÌfico trace( "Soldier interface. Destruye!" ); //Utiliza alguno de los mÈtodos del animal subject.eat( ); } public function moveTo( ) { //Comportamiento especÌfico trace( "Soldier Interface. moveTo" ); //Utiliza alguno de los mÈtodos del animal subject.moveLegs( ); } }

Y el rol de campesino:

class PeasantRole extends Role implements IPeasantActions { private var subject: IBasicActions ; function PeasantRole( subject: IBasicActions ) { this.subject = subject; trace( "Se crea el rol de campesino" ); } public function driveTo( ) { //Comportamiento especÌfico trace( "Lo llevo " ); //Utiliza alguno de los mÈtodos del animal subject.moveArms( ); subject.moveLegs( ); } public function doGardening( ) { //Comportamiento especÌfico trace( "Vale, cuido el jardÌn" ); //Utiliza alguno de los mÈtodos del animal subject.moveArms( ); } }

Ambas clases ( SoldierRole y PeasantRole ) implementan, respectivamente, los interfaces ISoldierActions e IPeasantActions. Esos son los interfaces que parecer· que implementa la clase Sheep

Mira:

var sheep: Sheep = new Sheep( ); var soldierSheep: ISoldierActions = ISoldierActions( sheep.getExtension( "SoldierRole" ) ); soldierSheep.destroy( ); var peasantSheep: IPeasantActions = IPeasantActions( sheep.getExtension( "PeasantRole" ) ); peasantSheep.doGardening( );

( InsÈrtense treinta minutos de risas histÈricas aquÌ ). Pero, un momento!!!. El Profesor Dispar se acaba de dar cuenta de un pequeÒo problema! ( bueno, en realidad no es un problemas, es una soluciÛn difÌcil )

La clase Sheep tiene conocimiento de sus extensiones ( los roles ). Esas extensiones se guardan en variables de clase, asÌ que no hay forma de aÒadir alg˙n rol, o modificar los ya existentes en tiempo de ejecuciÛn

Por tanto, el Profesor decide refactorizar la clase Sheep. Esta clase ya no guardar· sus posibles extensiones en variables de clase, sino que manejar· una colecciÛn ( un HashMap, un objeto ) con esos roles. De esta forma, ser· posible aÒadir o eliminar extensiones ( roles ) cuando se necesite ( no te creerÌas cÛmo se est· riendo ahora ).

El nuevo interfaz ISubject:

interface ISubject { public function getExtension( extName: String ): Role; public function addExtension( extName: String , extension: Role ); public function removeExtension( extName: String ); }

Y la nueva clase Sheep ( no me gusta un pelo la forma en la que el Profesor ha implementado la colecciÛn, utilizando un objeto, pero bueno... )

class Sheep implements ISubject, IBasicActions { private var rolesCol: Object; function Sheep( ) { trace( "I'm a sheep" ); rolesCol = new Object( ); } public function getExtension( extName: String ): Role { return rolesCol[ extName ]; } public function addExtension( extName: String, extension: Role ) { rolesCol[ extName ] = extension; } public function removeExtension( extName: String ) { rolesCol[ extName ] = null; } public function moveArms( ) { // movement implementation trace( "the sheep moves one arm" ); } public function moveLegs( ) { // movement implementation trace( "the sheep moves one leg" ); } public function eat( ) { // implements the way sheeps eat trace( "munch. munch" ); } }

Y el Profesor Dispar puede hacer algo como:

var sheep: Sheep = new Sheep( ); sheep.addExtension( "SoldierRole", new SoldierRole( sheep ) ); var soldierSheep: ISoldierActions = ISoldierActions( sheep.getExtension( "SoldierRole" ) ); soldierSheep.destroy(); sheep.removeExtension( "SoldierRole" ); sheep.addExtension( "PeasantRole", new PeasantRole( sheep ) ); var peasantSheep: IPeasantActions = IPeasantActions( sheep.getExtension( "PeasantRole" ) ); peasantSheep.doGardening( );

Por tanto, los clones creados por la m·quina para clonar pueden cambiar su aspecto en tiempo de ejecuciÛn ( primero pueden ser soldados, luego campesinos, m·s tarde lo que al Profesor le dÈ la gana ). El profesor ha conseguido mantener la abstracciÛn Oveja limpia de operaciones especÌficas para un cliente. Eso tambiÈn lo podrÌa haber conseguido gracias a la herencia, definiendo las operaciones para los clientes en las subclases, pero eso habrÌa generado una jerarquÌ difÌcil de manejar

La siguiente aventura del Profesor Dispar ser· m·s f·cil, lo prometo. °Incluso los genios del mal necesitan vacaciones!

[Nota importante]. Auto crÌtica: El cÛdigo para recuperar el rol activo no pasarÌa ning˙n control de calidad. DeberÌa implementarse de otra forma, el uso de una cadena de texto como clave es bastante peligroso

Abril 03, 2005

[OT] El nuevo miembro de la familia design-nation

newton.jpg

SÌ, es una Newton ( un MessagePad 120 ). Y como se puede ver en la fotografÌa, todavÌa funciona.

Es increÌble cÛmo este dispositivo se adelantÛ a su tiempo. Puede funcionar en modo apaisado, puede sincronizar con un Mac o un PC, soporta tarjetas PCMCIA...

TambiÈn es increÌble ver la devociÛn que se profesa por este cacharrito. Hay muchÌsimos foros activos, listas de correo, y sitios web dedicados a Èl.

newton_mini.jpg

newton_ipaq.jpg

Las especificaciones de Hardware:

* Ram: 2mb
* Rom: 8mb(upgrade)
* Processor: ARM 610 20MHz
* Newton OS: 1.3, 2.0
* Screen: 320x240, no backlight
* Release: 11/95
* Extra Notes: NOS 2.0 would only run if the unit had a rom upgrade.

Me voy a jugar un poco con Èl. Disfrutad el domingo!

Por cierto, una curiosidad: Èsta es la nota de prensa de su lanzamiento en EspaÒa