« Un ejemplo del patrÛn Extension Objects ( la versiÛn Java ) | Inicio | Un ejemplo del patron Command »

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...

TrackBack

URL del Trackback para esta entrada:
http://ctarda.dreamhosters.com/cgi-bin/mt-tb.cgi/681