ThinkGeek - Cool Stuff for Geeks and Technophiles

April 06, 2005

An example of the Extension Objects pattern

Do you remember Professor Coupling?. Do you remember his evil plans to conquer the world?.

Today, we’ll see how the Extension Objects pattern ( or “how to change the interface that a class implements at runtime” ) has helped Professor Coupling. It won’t be an easy task, because this is a complex pattern, but who said that being an evil genius was easy?.

If you remember the last post, Professor Coupling designed a cloning machine that was able to clone any given animal, without knowing it’s race ( or type ), just delegating the creation details to the animal itself.

So that’s the point where our story starts today. Professor Coupling has cloned yet ten thousand sheeps, and nine thousand cows ( he said something like “that’s the exact number I need, muhahahahahahhhhaaaaaa” ). But suddenly, he realizes that he is going to need a lot of sheeps, yes, and a lot of cows, but not all the sheeps nor all the cows will have the same role. Why?. Well, conquering the world is not easy. You need to organize things very well. You need a plan ( an evil plan ).

And Professor Coupling has a plan. He wants to train some of the sheeps, and some of the cows, as his army. He wants them to kill, without asking questions ( sorry for the violence, that’s what happens when you are crazy ). But he also wants some of the sheeps and cows to work in the factories, building weapons. And he also needs some of those sheeps and cows working in the countryside, growing vegetables to feed his army ( remember, Professor Coupling is crazy, but the is not an idiot ). He needs to assign different roles to the clones he has created.

soldierSheep.jpg
A SoldierSheep. No comment.

[Note]. Just to keep the different posts separated, each one showing an example of a pattern and only a pattern, I will start writing the Sheep and Cow classes from scratch. But, Professor Coupling will just add the new code to those classes.

So, the evil genius starts to think: “Right, I have a class that represents a Sheep. And now I need a sheep that behaves like a soldier, and another one that behaves like a peasant, and the same with the cows.”. That sounds like extending the functionality of a class, doesn’t it. That’s exactly the same that Professor Couple thinks. Brilliant!!!. “I will write a SoldierSheep class that extends Sheep, and another class, PeasantSheep, that also extends Sheep” ( insert five minutes of hysterical laughs here ).

But before starting writing code, Professor Coupling notices that maybe that’s not the best solution, because anytime he wants to assign a new role to a Sheep, he will have to write a new subclass ( for instance, an EngineerSheep, or a MaitreSheep,… ). And he doesn’t know in advance which roles he will need to assign to a sheep. It all depends of what he needs in the future. He wants to support the addition of unforeseen interfaces to the Sheep class ( isn’t he a genius? ).

He also notices that this approach has another weak point. He will not be able to re-assign a role. If he creates a SoldierSheep, that will be a SoldierSheep forever, no matter if he needs it to grow onions ( he is crazy, remember, sometimes he has very strange ideas ).

He also notices another subtle weak point. A SoldierSheep and a Sheep are exactly the same: sheeps. The only difference is that one of them has a particular behaviour, but in essence, they are the same.

Professor Coupling is crazy, but he’s not an idiot ( I know, I know, you knew that ). So, he believes he has found enough weak points in his initial idea to start searching google for another solution.

peasantSheep.jpg
A PeasantSheep. Believe it or not, it's enjoying its spare time gardening!

And after a few attempts, he finds a book (Pattern Languages of Program Design, Volume 3, Addison-Wesley, 1998. ), where Erich Gamma ( yes, one of the GoF authors ) wrote a chapter about the Extension Objects pattern.

This pattern intents to anticipate the extension of an object’s interface in the future ( that means: new interfaces can be added to a class at runtime ).

So he starts reading, and laughing, And the more he reads, the more he laughs!!.

The idea is quite simple. A class ( Sheep ) will be able to change the interface it implements at runtime, selecting that interface from a collection of objects ( the Extension Objects ). Each one of those objects will encapsulate one of the roles ( SoldierSheep, PeasantSheep,… ). ( At this moment, Professor Coupling has been laughing for nearly twenty minutes!! ).

How will the Sheep class select the interface to implement?. And how will the Cow class do it?. Well, both classes will implement the following interface:

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

The Sheep also implements an interface to encapsulate the actions it implements as an animal ( eat, moving the legs and arms… )

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

So, the Sheep class will be

class Sheep implements ISubject, IBasicActions
{
 private var soldierRole: SoldierRole;
 private var peasantRole: PeasantRole;
    
 function Sheep( )
 {
  trace( "I'm a sheep" );
 }
     
 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( )
 {
  // 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" );        
 }    
}

Notice how that class implements the getExtension method, choosing the class it should return from a collection of roles ( that are instance variables ). And the roles?

Here’s the base Role ( I have not implemented anything, but any common functionality to all the roles should be implemented here ).

class Role
{
 function Role( )
 {
 }
}

So, the SoldierRole:

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

And the PeasantRole:

class PeasantRole extends Role implements IPeasantActions
{
 private var subject: IBasicActions ;
 
 function PeasantRole( subject: IBasicActions )
 {
  this.subject = subject;
          
  trace( "PeasantBehaviour created" );
 }
 
 public function driveTo( )
 {
  //Specific behaviour
  trace( "I drive to " );
          
  //Uses some of the animal's actions
  subject.moveArms( );
  subject.moveLegs( );
 }
 
 public function doGardening( )
 {
  //Specific behaviour
  trace( "OK, gardening" );
          
  //Uses some of the animal's actions
          
  subject.moveArms( );
 }	
}

Both classes ( SoldierRole and PeasantRole ) implement two different interfaces ( ISoldierActions and IPeasantActions ). Those are the interfaces that the Sheep class will seem to implement.

Look:

var sheep: Sheep = new Sheep( );

var soldierSheep: ISoldierActions = ISoldierActions( sheep.getExtension( "SoldierRole" ) );

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

( Insert thirty minutes of hysterical laughs here ). But, hey!!. Wait!!. Professor Coupling has noticed a little problem ( well, it’s not a problem, really, it’s a “difficult solution” ).

The Sheep class knows its extensions ( the roles ). Those extensions are stored in instance variables, so there’s no way to add or remove extensions at runtime.

So he decides to refactor his Sheep class. This class will no longer store its possible extensions in instance variables, but it will manage a collection ( a HashMap, an Object ) that will store them. So, it will be possible to add or remove extensions whenever it’s needed. ( you wouldn’t believe how he’s laughing now ).

So, he refactors the ISubject interface:

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

And the Sheep class ( I don't like the way I've implemented the collection, using an object, but ... )

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

And Professor Coupling can do something like:

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

So, the clones created by the cloning machine are able to change the way we look at them at runtime ( first they can be soldiers, then they can be peasants, then whatever role Professor Coupling wants ). He has kept the Sheep abstraction unpolluted with operations that are specific to a client. He could have done so subclassing, defining the client operations in those subclasses, but that will create a hierarchy too difficult to manage.

The next Professor Coupling’s adventure will be easier to follow, I promise. Even evil geniuses need some vacations!.

[Important note]. Some self-criticism. The code that retrieves the roles won’t pass any quality check. Using a string as a key value to set and get those roles is very dangerous.

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

hello,

what i don't understand is, the soldierSheep is not of type sheep, so in fact it isn't a sheep. it has a reference to the sheep, but for example i am not able to call: soldierSheep.eat(); So this doesn't really sound like an extension to the class sheep to me, or am i getting something wrong here?

Posted by: Sven en: April 6, 2005 09:19 AM

another note, the soldierSheep doesn't even have reference to the sheep, but just to its basic actions, so it is in fact impossible to access any methods of a sheep over the soldierSheep instance. Right?

Posted by: Sven Busse en: April 6, 2005 09:22 AM

No, you can't call soldierSheep.eat( ), because eating is not part of the specific behaviour of a Soldier ( in my example ), but it is part of the Sheep general behaviour.

This pattern is not exactly a way to extend a class, it's more a way to make it look like it has changed its interface.

The soldierSheep has a reference to the sheep's public interface, which is an interface provided by us, so, yes, it will only have access to the methods that are part of that interface, which are all the sheep's public methods ( or the methods that we want to make public ), so that souldn't be a problem.

Posted by: Cesar Tardaguila en: April 6, 2005 09:25 AM

Hi,

Thanks for this thread. It's the first implementation in actionscript I've ever read.

One question though.
What is the purpose of implementing *Actions interfaces.
Don't you think is over architecturing ?

The code could be like that :

var sheep: Sheep = new Sheep( );
sheep.addExtension( "SoldierRole", new SoldierRole( sheep ) );
// upcast to the child class
var soldierSheep: SoldierRole = SoldierRole( sheep.getExtension( "SoldierRole" ) );
soldierSheep.destroy();

I don't see any good reason to separe the role from the actions.
Can you enlight me about that construct, please.
I should have omiited one point…

Thanks by advance.

It's a really clever design that could be thought as trade .
One is from a specific gender, but could have multiple jobs.

Posted by: erixtekila en: August 4, 2005 10:10 PM

Hi, erixtekila.

Well, maybe you are right, it could be a bit overkill to implement those interfaces, at least in this example, altough I'd like to maintain it. I did it that way just because any client of those roles doesn't need to knoe anything about how those roles are implemented, it just needs to know its public methods.

Posted by: Cesar Tardaguila en: August 4, 2005 10:28 PM

erixtekila: +1

Read my mind :)

Posted by: Ben Jackson en: August 5, 2005 07:00 PM