ThinkGeek - Cool Stuff for Geeks and Technophiles

April 26, 2005

An example of the Command Pattern

Everything is ready. The sheeps and the cows are cloned, and their roles have been assigned. It’s time for Professor Coupling to launch the final attack. It’s time to conquer the world!!!.

But how will Professor Coupling give his troops the order to launch the attack?.

If you remember the previous posts, Professor Coupling is able to clone any animal, and then, he is able to assign that cloned animal its role. So, Professor Coupling has cloned sheeps and cows, and now his army is composed of soldier sheeps, peasant sheeps, solider cows and peasant cows ( it’s not easy to conquer the world ).

Professor Coupling is crazy, but he’s not an idiot. He wants to conquer the world, sure, he has an evil plan to do it, sure, but he knows that having a good plan ( even if it’s an evil plan ) is not a guarantee to success. He needs an emergency plan.

He wants that some of the soldier sheeps take part in the first ( and glorious ) attack. But he wants some other soldier sheeps to rest while their mates die in the battlefield ( sorry again, this is very violent, I know, but, you know, he’s trying to conquer the world ), and to be ready to serve as reinforcements.

How could Professor Coupling manage this? Well, he is very busy, so the attack must be launched easily. Something like pressing the “attack” button will be perfect. It’s quick, it’s easy, and he can delegate the action of pressing the button to anyone of his subordinates ( muhahahahahahahaha ). That will be perfect, but only if he finds the way to tell every single Sheep what it is supposed to do when the "glorious moment" comes.

But how?. The knowledge that Professor Coupling has about the Soldier Sheeps is their interface. He knows that every soldier sheep implements and interface called ISoldierActions ( please, take a look at the post about the Extension Objects pattern, well need it ). What he really wants is that some sheeps execute one of the methods of ISoldierActions, and some sheeps execute a different one.

Let’s try to explain it with an example. Here are the ISoldierActions interface and the SoldierRole class ( they are a bit different from the ones that appeared in the last post ):

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" );
 }
}

And here are IPeasantActions and PeasantRole are exactly as they were

So, Professor Coupling has cloned ten thousand Soldier Sheeps, and ten thousand Peasant Sheeps, and he uses two arrays to hold a reference to all of them.

So, when he presses the "attack" button, he could do something like:

class ProfessorCoupling
{
 
 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( );	
  }		
 }	
}

And in the first frame of the main timeline:

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 );
}

ProfessorCoupling1.attack( soldiers, peasants );

( Insert some hysterical laughs here, please ). You know what comes now, don’t you?. Professor Coupling is crazy, but he’s not an idiot. He doesn’t like the solution he has found. Why?. Well, it’s not what he wanted. He just wanted to say "attaaaaaaaaaaaaaaaaaaaaaaack", and laugh hysterically while the sheeps obey his orders.

He feels that everything will be much easier if he could give every sheep an envelope containing its orders. When the moment of glory comes, he will just have to tell every sheep to open the envelope and obey the orders it contains. But he doesn’t what to know what he is requesting the sheep to do, and if fact, he doesn’t even what to know if he’s requesting something to a sheep or a cow, or whatever.

oveja_sobre.jpg

And then, he remembers when he was a young student, and read the GoF book. And he starts to laugh. He has remembered the Command Pattern.

He wants to give four different orders. Two of them will have to be obeyed by the Soldier Sheeps ( "attack", and "wait where you are until you receive more orders" ), and the other two by the Peasant Sheeps ( "start gardening", and "drive your truck home" ).

So he’s going to encapsulate the order, and the receiver of the order in a package ( the envelope ). How?. Look:

First of all, he will write the following interface:

interface ICommandActions
{
 public function execute( );
}

And the different commands will be:

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( );	
 }
}

So, now, when Professor Coupling presses the "attaaaaaaaaaaaaaaack" button, he will have to do something like:

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

That method receives as a parameter an array containing all the commands. That array could be built with a code similar to this:

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 );
}


ProfessorCoupling.attack( theArmy );

That code was awful, but it serves its purpose, which is to show how Professor Coupling has created a collection of objects, each one encapsulating a command and the receiver of that command. Now, Professor Coupling doesn’t need to know anything about the commands or about the receiver of those commands. He just has to say "hey, command, execute yourself", and the command will do the rest.

In fact, Professor Coupling has been able to decouple three different process: the object creation ( implementing the prototype pattern ), the assignment of roles to those objects ( the extension objects pattern ), and the way that those roles are “executed” ( the command pattern ).

Maybe he’s really a genius...

Posted by Cesar Tardaguila Date: April 26, 2005 07:04 AM | TrackBack
Comments

lol this is a fantastic article dude, classic way of actually keeping your attention while reading patterns (No easy task as you can easily find yourself getting bogged down into OO*PI/2-ZenPowers Theories).

Good read.

Posted by: Scott Barnes en: April 26, 2005 11:47 AM

Hi Cesar!
I liked very much tracking your articles about patterns, but its very painfull to try them just with CTRL+C CTRL+V from the above code. Any chances you post the packages all put togheter (.FLA and .AS), 'for the sake of the weak who belives in Professor Coupling madness...'

Best regards.

Dedé

Posted by: Dedé en: April 27, 2005 05:49 AM

You are right, Dedé. Form now on, I'll add a link to the source code.

Thank you for the suggestion.

Posted by: Cesar Tardaguila en: April 27, 2005 06:20 AM

Thanks Cesar, this is really cool! Indeed, you can post the above source-code if you like ;)

Cheers

Dedé

Posted by: Dedé en: April 27, 2005 06:55 PM