March 29, 2005
An example of the Prototype Pattern
Professor Coupling is a Spanish scientific that is planning to conquer the world. Do you want to read about the problems he encountered, and how he solved them using the prototype pattern?Professor Coupling was a respected Spanish scientist, that became crazy because of the bad working conditions that all the Spanish scientists must suffer ( well, and because his girlfriend started to date another man ). So he became an evil genius, and now, he only thinks about conquering the world!!!.
Professor Coupling
So, he has developed a cloning machine. A very special cloning machine, because it can be programmed in actionscript.
Professor Coupling’s plan is to clone as many sheeps (Any of various usually horned ruminant mammals of the genus Ovis in the family Bovidae ) as possible ( I don’t know exactly why, remember, he’s a genius, but he’s crazy ).
A sheep
So, the cloning machine looks like this:
class CloningMachine { function CloningMachine( ) { } public function buildClone( ): Sheep { return new Sheep( ); } public function buildManyClones( cloneNum: Number ) { var returnArray: Array = new Array( ); for( var k=0; k< clonesNum; k++ ) { returnArray[ k ] = new Sheep( ); } return returnArray; } }
And the sheep will be like:
class Sheep { function Sheep( ) { //this is a spanish sheep, so it sounds like a spanish sheep! trace( "beeeee. I'm a new Sheep" ); } }
After flooding the world with sheeps ( it was his evil plan, not mine ), Professor Coupling noticed that cloning a few thousands of cows could help him to conquer the world faster. But his cloning machine was not built to clone cows, it was just built to clone sheeps.
A cow
So, he decides to add the functionality needed to clone cows. How?. Look:
class CloningMachine { function CloningMachine( ) { } public function buildClone( ): Sheep { return new Sheep( ); } public function buildCowClone( ): Cow { return new Cow( ); } public function buildManyClones( cloneNum: Number ) { var returnArray: Array = new Array( ); for( var k=0; k< clonesNum; k++ ) { returnArray[ k ] = new Sheep( ); } return returnArray; } }
Professor Coupling is crazy, but he is not an idiot. He soon realizes that he’s going to be in trouble if he wants to clone other animals, like, for instance, birds, or even humans.
After thinking about the problem carefully, Professor Coupling gives it another try:
class CloningMachine { function CloningMachine( ) { } public function buildClone( type: String ): Object { if( type == "sheep" ) { return new Sheep( ); } else if ( type == "cow" ) { return new Cow( ); } } public function buildManyClones( cloneNum: Number ) { var returnArray: Array = new Array( ); for( var k=0; k< clonesNum; k++ ) { returnArray[ k ] = new Sheep( ); } return returnArray; } }
After a few minutes of hysterical laughs, Professor Coupling begins to think about how he has implemented his cloning machine, and he soon finds some weak points.
First, the buildClone method returns an Object, not a Sheep or a Cow. Why? Because this method doesn’t know what it’s going to create. Professor Coupling ( remember, he’s crazy, but he’s not an idiot ) feels that may not be the best way to solve the problem.
And he also notices that if he wants to clone another animal, he will have to change his cloning machine again. Hmm, that’s not easy to maintain.
But then, an idea begins to reach his brain. He doesn’t know exactly how, but what could happen if he could give the cloning machine an animal ( a sheep, a cow, a human, whatever animal ), and tell the machine: “give me 500 like this one”. Brilliant!. He will never have to worry about how the machine works, it will just give him as many copies of an animal he provides as needed.
Professor Coupling then remembers when he was a student, and read a book titled “Design patterns: Elements of reusable object-oriented software” ( the GoF ), and that thing called “The Prototype pattern”.
So, the cloning machine cannot know what to clone and how to clone it. That will be a responsibility of any animal. The cloning machine will just receive an animal, and will tell him to clone itself as many times as needed, and then will return the resulting clones.
So, ( remember, ActionSctipt does not support abstract classes ), any animal could implement an interface, that contains only one method:
interface ICloneable { public function clone( ): ICloneable; }
[ Note ].It will also be possible that all the different animals will extend a base class, so the return type of clone( ) will be that base class. But I will continue my example implementing an interface because I think it’s more flexible.
That method ( clone ) will be the one that will create and return a new copy of any single animal that implements it.
So, here’s a sheep:
class Sheep implements ICloneable { function Sheep( ) { trace( "Hi, I'm Dolly, I will be cloned soon" ); } public function clone( ): ICloneable { trace( "Beeee, I'm a new sheep" ); return new Sheep( ); } public function toString( ): String { return "{ Hi, I'm a sheep. }"; } }
And here’s a cow:
class Cow implements ICloneable { function Cow( initFlag: Boolean ) { trace( "Muuuu, I'm a cow. I will also be cloned soon" ); if( initFlag == true ) { init( ); } } private function init( ) { trace( "I'm an initialized cow, whatever that means" ); } public function clone( ): ICloneable { return new Cow( true ); } public function toString( ): String { return "{ Yes, I'm a cloned cow, muuuuuu }"; } }
And Professor Coupling will want to do something like this:
var cMachine: CloningMachine = new CloningMachine( ); cMachine.buildClone( new Sheep( ) ); cMachine.buildClone( new Cow( ) ); trace( "--" ); var sheepClones: Array = cMachine.buildManyClones( 5, new Sheep( ) ); var cowClones: Array = cMachine.buildManyClones( 5, new Cow( ) ); trace( "--" ); trace( "sheepClones " + sheepClones ); trace( "cowClones " + cowClones );
So, finally, the cloning machine will be able to receive an animal ( an instance of a class ), and tell it to create as many copies of itself as needed:
class CloningMachine { function CloningMachine( ) { } public function buildClone( template: ICloneable ): ICloneable { return template.clone( ); } public function buildManyClones( clonesNum: Number, template: ICloneable ): Array { var returnArray: Array = new Array( ); for( var k=0; k< clonesNum; k++ ) { returnArray[ k ] = template.clone( ); } return returnArray; } }
So, Professor Coupling can keep on flooding the world with the clones created by his machine, knowing that he is able to create copies of any thing he wants, because he has given his cloning machine the ability to generate objects whose type is unknown.
After some further reading, Professor Coupling also realizes that he has separated the code that handles the details of creating the new animals from the code that actually creates them ( that means, that, for example, if he wants to create 1000 red sheeps today, and 1000 blue sheeps tomorrow, he only has to put a painting machine next to the cloning machine, and that machine will handle the way that those sheeps are painted, without knowing how they were created ).
Thanks to Celia Carracedo for the drawings of the sheep, the cow, and evil Professor Coupling.
Posted by Cesar Tardaguila Date: March 29, 2005 07:33 AM | TrackBackgreat!! more of that :)
Posted by: jules en: March 29, 2005 11:29 AMVery nice. AS developers need more pattern examples like this. Maybe I'll tackle one some time.
Posted by: Keith Peters en: March 29, 2005 02:03 PMProfessor Coupling rules
Posted by: Javier Tardáguila en: March 29, 2005 03:58 PMA great post !
I liked your writing so much that i'm going to feed your RSS link to my Feedreader :-)
Keep it up.
Posted by: Silent Walker en: April 21, 2005 10:51 AMnice explanation, i think i got it, but as ususal, teory is diferent froma practice..
i think it would be nice to have a downloadable .zip, or any file, that show reall time how this works...
by showing sheeps and cows on stage using the "buildManyClones() method"
and for us to see the code of the classes...
Thanks... was a usefull post...
IsaacDM
Posted by: IsaacDM en: August 5, 2005 08:48 PM