PDA

View Full Version : Factory Design Pattern


Flash Gordon
08-13-2007, 09:12 PM
Does anybody actually use Factory Design Pattern?

Seems like a load of whoey, or O'Reilly's team isn't very good at explaining anything within a practical application.

senocular
08-13-2007, 09:21 PM
I do. My Mii Editor ( http://www.miieditor.com ), for example, uses a factory to create instances to manage the loading and saving of files. A factory is used because depending on whether or not the application is being run as an AIR app or from a web page, different classes are used to handle the load and save operations. Its the factory's responsibility to determine which instance is needed and returns that to the main application.

Flash Gordon
08-13-2007, 09:25 PM
Okay. thanks for the response. I wont rule it out then.

Do you have a suggestion for a small application that would/could/should use this pattern that I can try to build so I can post it up for criticism?

senocular
08-13-2007, 09:29 PM
As in an excuse to write something that uses it?

Flash Gordon
08-13-2007, 10:03 PM
Yea, i can't seem to wrap my head around its usefulness or what would I need to use it for or when.

senocular
08-13-2007, 10:11 PM
Basically its when you have a need for one or more objects with you having a collection of many objects that facilitate the need but given your current circumstances should only be using one type of those objects.

For example, you need to display a set of OS components on the screen but, as is the case with Flash, your environment does not support native OS widgets. So what you have done is recreated each supported OSs' widgets for your application from scratch. However you will only want to display your Mac OS widget components when playing on the Mac and Windows OS widget components when on Win. To do that you would have two factories, a WinComponent factory and a MacComponent factory each of which create the same widget components with the same functionality but does so in 2 different visual (and behavioral) styles. Which factory to use will be determined at startup when the OS is detected.

var componentFactory = (OS == "Mac") ? MacComponentFactory : WinComponentFactory;
addChild(componentFactory.newButton());
...

Flash Gordon
08-13-2007, 10:21 PM
Ok, I think I understand it a little. So an other example might be:

A users enters their birthday. Based upon their age, their avatar character is either a baby, teen, young adult, or old fogy. All of these characters would extend a basic "Character" abstract class and therefore share some of the same method signatures.

The point of a Factory Pattern is to loosen the coupling between the client and the class, correct? When using a Factory pattern adding additional stuff later on should be easier and require no changes to current code, but adding more code?

senocular
08-13-2007, 10:23 PM
I think you're getting it : )

Flash Gordon
08-13-2007, 10:25 PM
cool. Let me see if I can find a way to put this to use and post up an example in a day or two.

Thanks senocular for the help. The concepts of OO had be kind of hard to wrap my head around as they are never a direct line of sight...always the long way around.

hangalot
08-14-2007, 01:10 PM
senocular's example is what you call an abstract factory. this is where you create a class according to your needs that has the same methods as an interface which all these class you can create implements, and this class gives you the specific feedback you desire.

this is prob the least used type of factory. a different type of factory is the factory method.

lets say for example i havce a piece of xml that defines which controls i want to display, the my factory method will just be a switch that returns the correct type of control from some static function.

two different factory types for different levels of complexity.

abstract factories are especially used for tokanizers and parsers, while a factory methods occur all over most peoples code without them knowing what it is.
wheneevr you see a method

public function getThing(v:Number):ISomething
{
switch(v)
{
case 0:
return new LittleThing() as ISomething;
break;
case 1:
return new BigThing() as ISomething;
break
}

}





thats a factory (method)!

senocular
08-14-2007, 03:45 PM
My first example (load/save) was factory method.

They're very similar, though. 1 factory with 1 type, or multiple factories with multiple types.

hangalot
08-14-2007, 03:48 PM
ah, did not follow the link, just saw the code later and assumed it was the same type :)

factory methods and abstract factories are very handy info however, a tut would be handy for people i think (and you excell at that!!) as itemrenderers and the Ifactory interface in flex seems to be a huge problem for most people conceptually

senocular
08-14-2007, 04:13 PM
The link wouldn't have gotten you anything, but it does use it ; )

I was thinking about maybe doing some pattern tutorials, but they seem to be time consuming and most people aren't really up to a level to take much advantage of it. Those that are can usually get by with the other descriptions and examples out there on the web for C or Java etc.

hangalot
08-14-2007, 04:15 PM
true. maybe just put a link to head first DP on your site and say give me money whilst buying this at amazon ;)

tg
08-14-2007, 04:29 PM
here is a great sight for design patterns.
the code examples are c# but you shouldnt have a problem reading / understanding them.
they have explanations and examples of both abstract factory, and factory method

http://www.dofactory.com/Patterns/Patterns.aspx

Flash Gordon
08-14-2007, 09:56 PM
URGH...I'm just not getting it.
I don't see how this provides any advantage. It takes longer and is just more code for nothing:

I understand the inheritance part here, but not the factory.


/**
* Abstract class not to be directy instansiated
*/

package instruments
{

public class Musician
{

public function play():void {}

public function stop():void
{
trace("stopped playing");
}

public function winkAtAudience():void
{
trace("Hey Baby! How you doin'?");
}
}
}



package instruments
{
import instruments.Musician;


public class Saxophonist extends Musician
{

public function Saxophonist()
{
//
}

override public function play():void
{
trace("jam on that sax, brotha");
}

public function changeReed():void
{
trace("this reed sucks. put on a new one");
}
}
}



package instruments
{
import instruments.Musician;


public class Trumpeter extends Musician
{

public function Trumpeter()
{
//
}

override public function play():void
{
trace("cheer-o ol' chap. what a fine day to play Handel.");
}

public function getCheck():void
{
trace("that will be $200, please");
}
}
}



package instruments
{
import instruments.Musician;


public class Pianist extends Musician
{

public function Pianist()
{
//
}

override public function play():void
{
trace("sing us a song, I'm the piano man");
}

public function makeJoke():void
{
trace("Yes, this is a 6ft pianist...");
}
}
}



package instruments
{
import instruments.Musician;

public class Creator
{
public static const SAXOPHONE:uint = 0;
public static const TRUMPET:uint = 1;
public static const PIANO:uint = 2;

public function Creator()
{
//
}

public function createMusician(type:int):Musician
{
switch(type)
{
case 0:
return new Saxophonist();
break;
case 1:
return new Trumpeter();
break;
case 2:
return new Pianist();
break;
}

return null;
}
}
}



package
{
import flash.display.Sprite;
import flash.text.TextField;
import flash.events.MouseEvent;

import instruments.Musician;
import instruments.Creator;

final public class DocumentClass extends Sprite
{
var musician:Musician;
var creator:Creator;

public function DocumentClass()
{
makeLabels();
creator = new Creator();
}

private function makeLabels():void
{
var sax:TextField = new TextField();
sax.text = "Saxophone";
sax.x = sax.y = 10;
addChild(sax);
sax.addEventListener(MouseEvent.MOUSE_DOWN, saxDown);

var tmp:TextField = new TextField();
tmp.text = "Trumpet";
tmp.x = 10
tmp.y = 30;
addChild(tmp);
tmp.addEventListener(MouseEvent.MOUSE_DOWN, tmpDown);

var pno:TextField = new TextField();
pno.text = "Piano";
pno.x = 10
pno.y = 50;
addChild(pno);
pno.addEventListener(MouseEvent.MOUSE_DOWN, pnoDown);
}

private function saxDown(event:MouseEvent):void
{
musician = creator.createMusician(Creator.SAXOPHONE);
musician.play();
}

private function tmpDown(event:MouseEvent):void
{
musician = creator.createMusician(Creator.TRUMPET);
musician.play();
}

private function pnoDown(event:MouseEvent):void
{
musician = creator.createMusician(Creator.PIANO);
musician.play();
}
}
}

senocular
08-14-2007, 10:20 PM
Normally you're not defining your own types/contstants. Using them to define your instances is going to just mean more work since you might as well just create the classes directly by name. Instead its usually a case of basing it off of environmental variables, like Capabilities.os (widget components example) or Capabilities.playerType (load/save AIR vs net example).

// factory method
package{
import flash.system.Capabilities;
class IOFactory {
public static function getSaver():ISave {
switch(Capabilities.playerType) {
case "desktop":
return new AIRSaver();
default:
return new NetSaver();
}
}
}

// usage
var saver:ISave = IOFactory.getSaver();
saver.save(this.getData());
Where both AIRSaver and NetSaver implement ISave and have the same interface. The responsibility of determining which saver to instantiate falls to the factory.

Flash Gordon
08-14-2007, 10:36 PM
Yea, that makes total sense and is easy to understand. In fact, it is the most logical way.

However, I was trying to imitate the example from the O'Reilly Design Patterns for AS 3 book. It looks some like this, and it just doesn't make much sense to me. At the very end, there is no user input that determines which ships to create, the just go ahead and make them from the factory by hand....Maybe I should just forget about this O'Reilly book and read stuff online....


package weapons
{
import flash.display.Sprite;
import flash.events.*;
// ABSTRACT Class (should be subclassed and not instantiated)
internal class Projectile extends Sprite
{
internal var nSpeed:Number;// holds speed of projectile
// ABSTRACT Method (must be overridden in a subclass)
internal function drawProjectile():void
{
}
internal function arm():void
{
// set the default speed for the projectile (5 pixels / fame)
nSpeed=5;
}
internal function release():void
{
// attach EnterFrame event handler doMoveProjectile()
this.addEventListener(Event.ENTER_FRAME,this.doMov eProjectile);
}
internal function setLoc(xLoc:int,yLoc:int):void
{
this.x=xLoc;
this.y=yLoc;
}
// update the projectile sprite
internal function doMoveProjectile(event:Event):void
{
this.y+= nSpeed;// move the projectile
// remove projectile if it extends off the top or bottom of the stage
if (this.y < 0 || this.y > this.stage.stageHeight)
{
// remove the event listener
this.removeEventListener(Event.ENTER_FRAME,this.do MoveProjectile);
this.stage.removeChild(this);// remove sprite from stage
}
}
}
}
//
//
package weapons
{
internal class HeroCannonBall extends Projectile
{
override internal function drawProjectile():void
{
graphics.beginFill(0xFFFF00);
graphics.drawCircle(0,0,5);
graphics.endFill();
}
override internal function arm():void
{
nSpeed=-10;// set the speed
}
}
}
//
//
package weapons
{
internal class AlienCannonBall extends Projectile
{
override internal function drawProjectile():void
{
graphics.lineStyle(3,0xFF00FF);
graphics.drawCircle(0,0,5);
}
override internal function arm():void
{
nSpeed=8;// set the speed
}
}
}
//
//
package weapons
{
import flash.events.*;
internal class AlienMine extends Projectile
{
override internal function drawProjectile():void
{
graphics.lineStyle(3,0xFF0000);
graphics.drawRect(-5,-5,10,10);
}
override internal function arm():void
{
nSpeed=2;// set the speed
}
override internal function doMoveProjectile(event:Event):void
{
super.doMoveProjectile(event);
this.rotation+= 5;// rotate
}
}
}
//
//
package weapons
{
import flash.display.Stage;
import flash.errors.IllegalOperationError;
// ABSTRACT Class (should be subclassed and not instantiated)
public class Weapon
{
public function fire(cWeapon:uint,target:Stage,xLoc:int,yLoc:int): void
{
var projectile:Projectile=this.createProjectile(cWeapo n);
trace("Firing " + projectile.toString());
// draw projectile
projectile.drawProjectile();
// set the starting x and y location
projectile.setLoc(xLoc,yLoc);
// arm the projectile (override the default speed)
projectile.arm();
// add the projectile to the display list
target.addChild(projectile);
// make the projectile move by attaching enterframe event handler
projectile.release();
}
// ABSTRACT Method (must be overridden in a subclass)
protected function createProjectile(cWeapon:uint):Projectile
{
throw new IllegalOperationError("Abstract method: must be overridden in a subclass");
return null;
}
}
}
//
//
package weapons
{
public class AlienWeapon extends Weapon
{
public static const CANNON:uint=0;
public static const MINE:uint=1;
override protected function createProjectile(cWeapon:uint):Projectile
{
if (cWeapon == CANNON)
{
trace("Creating new alien cannonball");
return new AlienCannonBall ;
}
else if (cWeapon == MINE)
{
trace("Creating new alien mine");
return new AlienMine ;
}
else
{
throw new Error("Invalid kind of projectile specified");
return null;
}
}
}
}
//
//
package weapons
{
public class HeroWeapon extends Weapon
{
public static const CANNON:uint=0;
override protected function createProjectile(cWeapon:uint):Projectile
{
if (cWeapon == CANNON)
{
trace("Creating new Hero cannonball");
return new HeroCannonBall ;
}
else
{
throw new Error("Invalid kind of projectile specified");
return null;
}
}
}
}
//
//
package ships
{
import flash.display.*;
import weapons.HeroWeapon;
import flash.events.*;
internal class HeroShip extends Ship
{
private var weapon:HeroWeapon;
override internal function drawShip():void
{
graphics.beginFill(0x00FF00);// green color
graphics.drawRect(-5,-15,10,10);
graphics.drawRect(-12,-5,24,10);
graphics.drawRect(-20,5,40,10);
graphics.endFill();
}
override internal function initShip():void
{
// instantiate the hero projectile creator
weapon=new HeroWeapon ; // <<<<------called here
// attach the doMoveShip() and doFire() methods on this object
// as MOUSE_MOVE and MOUSE_DOWN handlers of the stage
this.stage.addEventListener(MouseEvent.MOUSE_MOVE, this.doMoveShip);
this.stage.addEventListener(MouseEvent.MOUSE_DOWN, this.doFire);
}
protected function doMoveShip(event:MouseEvent):void
{
// set the x coordinate of the sprite to the
// mouse relative to the stage
this.x=event.stageX;
event.updateAfterEvent();// process this event first
}
protected function doFire(event:MouseEvent):void
{
weapon.fire(HeroWeapon.CANNON,this.stage,this.x,th is.y - 25);
event.updateAfterEvent();// process this event first
}
}
}
//
//
// more....


Where it ends with this:

// instantiate ship creator
var shipFactory:ShipCreator = new ShipCreator();
// place hero ship
shipFactory.addShip(ShipCreator.HERO, this.stage, this.stage.stageWidth / 2, this.stage.stageHeight - 20);

// place alien ships
for (var i:Number = 0; i < 5; i++)
{
shipFactory.addShip(ShipCreator.ALIEN, this.stage, 120 + 80 * i, 100);
}

senocular
08-14-2007, 10:56 PM
With that you're still maintaining a level of control on the side of the factory. In that case, however, and I don't think I saw the factory class in the code you posted, but the factory would have a determination in what each ShipCreator.[TYPE] would be given some variable, for example, game level. If the game is currently on level 3, perhaps all the ALIEN ships are of a tougher model. The main application doesn't have to concern itself with what kind of alien ship needs to be made based on the level, just that at this point in time, some kind of alien ship needs to be made. Of course in this example you also have the separation of type (HERO vs ALIEN) which if used as I explained, would be basing instantiation off a couple of "cases". And maybe that's all the book is showing, just the types and not levels or anything else. Its not the best example, but maybe they just wanted to show you what factories are and not so much how they are best used.

Flash Gordon
08-14-2007, 11:00 PM
Cool, Hopefully as my knowledge of other design patterns increase, all the patterns will make more sense. Right now, it is quite rough.

Thanks again for the help!
:)

hangalot
08-15-2007, 09:53 AM
flash, you are making an effort with this, this effort will pay off, all of us struggled with this at some stage. stick with it and don't give up!

also the head first design pattern book is the easiest book, the code is java, but there is so little code you will understand it easily.

a moment of clarity and it will all fall into place.

Flash Gordon
08-15-2007, 09:58 AM
I certainly hope so, buddy. I've gotten 150 pages down in the O'Reilly book and I'm still at a loss with stuff. The book raises more questions than what it gives me answers.

The concepts of OOP is by far the most difficult thing I have encountered in programming thus far. Making classes and encapsalation is easy. Making OOP is quite the art.

senocular
08-15-2007, 03:38 PM
I've heard good things about the head first book. That might be worth checking out.

hangalot
08-15-2007, 04:33 PM
i think the as3 pattern book is a waste (but thats a very personal opinion and nothing against the authors whom i rate personally), head first is a book designed to make understanding easy and the pattern book is a great book

Flash Gordon
08-15-2007, 06:47 PM
so it is said, and so it shall be.

is this the one you are referring to:
http://www.amazon.com/Head-First-Design-Patterns/dp/0596007124

MichaelxxOA
08-15-2007, 10:37 PM
Do remember, though, that this stuff IS in fact pretty difficult, and takes some time to understand. It's not often people go through any one book on some aspect of software design, and come out feeling like they understand it completely. I know for me, and quite a few other people, it takes a few times reading about something looking at a few types of approaches. In the end though, I always never truly appreciate what I've learned until I've thought it up in my own head as a solution to my own problem.

Encouragement? idk :-\... lol

-M

MichaelxxOA
08-15-2007, 10:59 PM
flash, you are making an effort with this, this effort will pay off, all of us struggled with this at some stage. stick with it and don't give up!

also the head first design pattern book is the easiest book, the code is java, but there is so little code you will understand it easily.

a moment of clarity and it will all fall into place.

And I 100% second that whole comment ;)

hangalot
08-16-2007, 09:46 AM
yeah thats the book.