ThinkGeek - Cool Stuff for Geeks and Technophiles

June 02, 2005

An example of the Observer pattern

Be aware!. Professor Coupling and his army of clones are ready!.The final attack is going to be launched quite soon!. And their weapons are actionScript and design patterns!

In the previous posts, we’ve seen how Professor Coupling is able to clone any animal, and how he’s able to assign different roles to those clones. Oh, and he has also found the way to assign any animal its orders. Muhahahahahaha

First of all, remember: Professor Coupling is crazy, but he’s not an idiot. He knows that maybe, his evil plan has some weak points, that maybe, there could be some mistakes. And he also knows that sometimes it’s better to go back to the starting point to gather some strength and launch a new attack ( well, you know, it’s not easy to conquer the world ). He wants to be able to tell his troops to cancel the attack if something goes wrong ( you know, please insert some hysterical laughs here ) .

So, he has equipped his troops with the newest model of EnigmaRadio ( enigmawoot!!??? ). The EnigmaRadio is the last invention of Professor Coupling’s evil genius, and basically it’s, well, a radio receiver, that is cheaper than an iPod. Not as cool as an iPod, but cheaper.

But why? Why would a sheep or a cow need a radio?. Well, conquering-the-world-battles are noisy. It’s not easy to communicate with your troops when you are in the middle of such destruction. There’s noise, there’s dust, fog… In fact, sometimes, you might be thinking that you are giving an order to a sheep, but you are talking to a cow. Well, a complete mess. And, of course, Professor Coupling is aware of all these communication problems.

Professor Coupling has been using his evil genius to find a solution. The solution he has found ( muhhhahahahahahah ) has been to assign a cow the responsibility of propagating his orders. In plain English, there will be a cow ( Corporal Cow ) whose duty will be to run all the way to the trenches, and then, one by one, give all the cows and sheeps the new orders. Brilliant!!!!.

Well, not so brilliant. What if someone kills ( sorry, gratuite violence again ) Corporal Cow when he still hasn’t given the new orders to all the soldiers?.

And so, Professor Coupling, once again, remembered when he was a young student, and the happy times when he studied the Observer pattern.

That’s why Professor Coupling, who is crazy but is not an idiot, has equipped all his troops with the latest model of the EnigmaRadio. The EnigmaRadio will we tuned to BBC Radio 4 ( why not? ). So, his troops will charge against the enemy with their radios switched on, and as soon as they hear “In the navy”, by Village People, they will now that it’s time to go back to their starting position.

enigmaBroadcaster.jpg
Dj Coupling broadcasting his program

The code will be something like this:

interface IObserver
{
 public function update(  info: Object ): Void;
}

interface ISubject
{
 public function addObserver( obs: IObserver ) : Boolean;
 public function removeObserver( obs: IObserver ) : Boolean;
 public function notifyObserver( info: Object ) : Void;
}

class Cow implements IObserver 
{
 function Cow( )
 {
  this.init( );
 }
 	
 private function debug( arg: String ): Void
 {
  trace( "Vaca | Cow: ->"+arg );
 }
 	
 private function init( ): Void
 {
  debug( "init" );
 }
 	
 public function update( info: Object ) : Void
 {
  debug( "La emisora del profesor ha emitido | song in the radio is: "+info.cancion ); 
  debug( enigmaResult( info.cancion ) );
 }
 	
 private function enigmaResult( arg: String ): String 
 {
  debug( "soy la máquina enigma interpretando qué hacer | I am the enigma machine" );
  if( arg != "In the Navy" )
  {
   return "sigo a lo mío. MUUU | nothing to do MUUUUU";
  }
  else
  {
   return "cielos, llegó el momento. MUUUUUUUUUUU | Oh, my God, it's the signal MUUU";
  }
  			
 }
 	
}

class Sheep implements IObserver 
{
 function Sheep( )
 {
  this.init( );
 }
 	
 private function debug( arg: String ): Void
 {
  trace( "Oveja | Sheep: ->"+arg );
 }
 	
 private function init( ): Void
 {
  debug( "init" );
 }
 	
 public function update( info: Object ) : Void
 {
  debug( "La emisora del profesor ha emitido | song in the radio is: "+info.cancion ); 
  debug( enigmaResult( info.cancion ) );
 }
 	
 private function enigmaResult( arg: String ): String 
 {
  debug( "soy la máquina enigma interpretando qué hacer | I am the enigma machine" );
  if( arg != "In the Navy" )
  {
   return "sigo a lo mío. BEEEEE | nothing to do BEEEEEE";
  }
  else
  {
   return "cielos, llegó el momento. BEEEE | Oh, my God, it's the signal BEEEEEE";
  }
  			
 }
 	
}

class Profesor implements ISubject
{
 private var arrayObservers: Array;
 	
 function Profesor( )
 {
  init( );
 }
 	
 private function debug( arg: String ) : Void
 {
  trace("Profesor: ->"+arg);
 }
 	
 private function init( ): Void
 {
  debug( "init" );
  this.arrayObservers = new Array(); 
 }
 	
 public function addObserver( obs: IObserver ): Boolean
 {
  debug( "registrando | registering" );
  this.arrayObservers.push( obs );
  return true;
 }
 	
 public function removeObserver( obs: IObserver ) : Boolean 
 {
  var longObservers: Number = this.arrayObservers.length;
  for ( var k: Number = 0; k< longObservers; k++ )
  {
   var tmpObserver: IObserver = this.arrayObservers[ k ];
   if( tmpObserver == obs )
   {
    this.arrayObservers.splice( k, 1 );
    break;
   }
   				
  }
  return true;
 }
 	
 public function notifyObserver( info: Object ): Void
 {
  var longObservers: Number = this.arrayObservers.length;
  for ( var k: Number = 0; k<longObservers; k++ )
  {
   var tmpObserver: IObserver = this.arrayObservers[ k ];
   tmpObserver.update( info );
  }
 }
 	
}

And the first frame of the main timeline

var pd: Profesor = new Profesor();
var myCow: Cow = new Cow( );
var mySheep: Sheep = new Sheep( );

pd.addObserver( myCow );
pd.addObserver( mySheep );

var info:Object = new Object();
info.cancion = "In the Navy";

pd.notifyObserver( info );

Muhahahahahahhahahahaha ( almost half an hour of hysterical laughs ). Professor Coupling has found a quick and reliable way of sending messages to his troops. It’s perfect!! Well, really?. Maybe it’s not so perfect.

Imagine that, for instance, Professor Coupling wants his soldiers to report back when they receive the new orders. Or imagine that Professor Coupling can infiltrate a sheep into the enemy’s headquarters. That spy could not communicate with Professor Coupling to tell him when the perfect moment to launch the attack comes!!!.

But, Professor Coupling, who is crazy but is not an idiot, has previously thought about it, and has decided to implement a mechanism in the EnigmaRadio that could serve his troops to communicate back with him. So, the spy sheep, as soon as it discovers the enemy’s weakest point, could phone the EnigmaRadio base station and ask them to broadcast Boney M’s "Rasputin" ( their secret "attttttaaaaaaaaaaaaaack" signal ). So, when Professor Coupling’s troops hear "Rasputin" through their EnigmaRadio, they will know that the moment has come, that it will be the moment to launch the final attack.

enigmaReceiver.jpg
A sheep singing "In the Navy"

So, Professor Coupling will have to change his code a bit ( but he doesn’t care, listen: "Muhahahahahahahaaaaa" ). He will start adding a new method ( requestInfo ) to the ISubject interface . That method will allow his radio station to receive the telephone calls from the Spy Sheep. He has also changed the constructor of the Cow and Sheep classes.

interface ISubject
{
 public function addObserver( obs: IObserver ) : Boolean;
 public function removeObserver( obs: IObserver ) : Boolean;
 public function notifyObserver( info: Object ) : Void;
 public function requestInfo(  arg: String ): Void
}

class Cow implements IObserver 
{
 	
 private var profesor: Profesor;
 	
 function Cow( prof: Profesor )
 {
  this.profesor = prof;
  this.profesor.addObserver(this);
  this.init( );
 }
 	
 private function debug( arg: String ): Void
 {
  trace( "Vaca | Cow: ->"+arg );
 }
 	
 private function init( ): Void
 {
  debug( "init" );
 }
 	
 public function update( info: Object ) : Void
 {
  debug( "La emisora del profesor ha emitido | song in the radio is: "+info.cancion ); 
  debug( enigmaResult( info.cancion ) );
 }
 	
 private function enigmaResult( arg: String ): String 
 {
  debug( "soy la máquina enigma interpretando qué hacer | I am the enigma machine" );
  if( arg != "In the Navy" && arg!="Rasputin" )
  {
   return "sigo a lo mío. MUUU | nothing to do MUUUUU";
  }
  		
  else
  {
   return "cielos, llegó el momento. MUUUUUUUUUUU | Oh, my God, it's the signal MUUU";
  }
  			
 }
}

class Sheep implements IObserver 
{
 private var profesor: Profesor;
 	
 function Sheep( prof: Profesor )
 {
  this.profesor = prof;
  this.profesor.addObserver(this);
  this.init( );
 }
 	
 private function debug( arg: String ): Void
 {
  trace( "Oveja | Sheep: ->"+arg );
 }
 	
 private function init( ): Void
 {
  debug( "init" );
 }
 	
 public function update( info: Object ) : Void
 {
  debug( "La emisora del profesor ha emitido | song in the radio is: "+info.cancion ); 
  debug( "enigmaResult: "+enigmaResult( info.cancion ) );
 }
 	
 private function enigmaResult( arg: String ): String 
 {
  debug( "soy la máquina enigma interpretando qué hacer | I am the enigma machine" );
  if( arg != "In the Navy" && arg!="Rasputin" )
  {
   debug("un momento, debemos atacar ");
   debug("wait a moment, we must attack");
   debug("pediré un ataque | I'll ask for an attack ");
   askForAttack( );
   return "he solicitado un ataque | i've asked for an attack";
   //return "sigo a lo mío. BEEEEE | nothing to do BEEEEEE";
  }
  else
  {
   return "cielos, llegó el momento. BEEEE | Oh, my God, it's the signal BEEEEEE";
  }
  	
 }
 	
 private function askForAttack( ): Void
 {
  this.profesor.requestInfo( "Rasputin" );
 }
 	
}

class Profesor implements ISubject
{
 	
 private var arrayObservers: Array;
 	
 function Profesor( )
 {
  init( );
 }
 	
 private function debug( arg: String ) : Void
 {
  trace("Profesor: ->"+arg);
 }
 	
 private function init( ): Void
 {
  debug( "init" );
  this.arrayObservers = new Array(); 
 }
 	
 public function addObserver( obs: IObserver ): Boolean
 {
  debug( "registrando | registering" );
  this.arrayObservers.push( obs );
  return true;
 }
 	
 public function removeObserver( obs: IObserver ) : Boolean 
 {
  var longObservers: Number = this.arrayObservers.length;
  for ( var k: Number = 0; k< longObservers; k++ )
  {
   var tmpObserver: IObserver = this.arrayObservers[ k ];
   if( tmpObserver == obs )
   {
    this.arrayObservers.splice( k, 1 );
    break;
   }
   				
  }
  return true;
 }
 	
 public function notifyObserver( info: Object ): Void
 {
  var longObservers: Number = this.arrayObservers.length;
  for ( var k: Number = 0; k<longObservers; k++ )
  {
   var tmpObserver: IObserver = this.arrayObservers[ k ];
   tmpObserver.update( info );
  }
 }
 	
 public function requestInfo( arg: String ): Void
 {
  var info: Object = new Object ();
  info.cancion=arg;
  notifyObserver( info ) ;
 }
 	
}

And in the main timeline:

var pd: Profesor = new Profesor();
var myCow: Cow = new Cow( pd );
var mySheep: Sheep = new Sheep( pd );

var info:Object = new Object();
info.cancion = "Any song";
pd.notifyObserver( info );

All these classes have been simplified to the maximum, because we just wanted to show how the pattern works, and to show that Professor Coupling is an evil genius, but he’s not an idiot ( the image fades to black, and "The End" appears ).

And remember, Professor Coupling is spanish, and a bit lazy, so he's left some comments in his mother tongue...

You can download the source code here

Posted by Javier Tardáguila Date: June 2, 2005 03:51 PM | TrackBack
Comments

Hows it going Cesar? Nice article! I just thought I'd let you know there are a lot of BR tags showing up in the source for some reason. Loving the professor analogy :)

Posted by: Richard Leggett en: June 2, 2005 05:22 PM

Great article!

Thanks and keep the patterns coming ;)

Posted by: Kim Hansen en: June 2, 2005 05:36 PM

Ooops!. A good catch. Thanks, Richard.

But in fact, I'm not Cesar, I'm Javier. He's enjoying some holidays ( he said something like "I really deserve this", or something similar ) before he left me doing the hard work.

Posted by: Javier Tardaguila en: June 2, 2005 05:36 PM

Woops. I'm so sorry I missed the name at the end. Nevertheless, quality article.

Posted by: Richard Leggett en: June 2, 2005 11:08 PM

Really good tutorial my friend.
The Prof helps you understand :)

Posted by: Darren Richardson en: June 3, 2005 10:54 AM

Very nice.

Posted by: Andy en: June 9, 2005 05:00 PM

Why not just use listner objects and eventDispatcher? They use the observer pattern.

Posted by: Todd Coulson en: August 5, 2005 05:30 PM

Sure, Todd, the eventDispatcher and the listener objects are just an implementation of the observer pattern.

We just wanted to show how the pattern works, and explain it so it could be more easily digested.

Posted by: Cesar Tardaguila en: August 5, 2005 07:09 PM