PDA

View Full Version : [example] Design Patterns:Factory Method


Flash Gordon
05-27-2009, 08:17 AM
Once again, I'm trying to present a practical use of another design pattern that I use in Flash. This is probably the pattern I use the most and it makes my life so much easier. May I present the Factory Method. (Let's not get into whether it's a real pattern or not).

I do a lot of the same things day in and day out for my projects. Seriously, how many times have you built a banner, video player, gallery, etc. I'm sure you can find something that you do on a consistent basis in Flash. Well, where you are doing that you can make the work easier by implementing the Factory Method. The goal is to abstract out the common logic into an abstract class (or delegate classes) and let the concrete class provide the assets, concrete instantiations, and unique logic. For me, I make a lot of galleries. So what is typically among my galleries? Well, I load images externally; I have a transition; I'm able to move throughout images; I often have thumbnails; the need to preload, etc... and the list going on.

Below I've written a watered down version of a sample gallery. If you look at AbstractGallery you can see most of the logic resides there and SimpleGallery merely only supplies the necessary assets to make AbstractGallery work. How does it do this? It does it by overriding the create methods to return an ITransition and collection of URLs to load. The rest of the logic is specific to that gallery. As you can see SimpleGallery is very minimal. It took me much longer to write AbstractGallery than it did SimpleGallery and writing SimpleGallery was much much easier too! Why, because once again most of the logic such as loading cues is in the abstraction. So what other benefits can I expect to see from using an AbstractGallery? Well for me, it's faster to code and time is money; I'm less likely to make mistakes because I'm supplying very little logic; I can have a lesser experienced flasher write another SimpleGallery with relative ease; I can make updates to AbstractGallery rather easily if I need to refactor, and the list goes on.

So let's look at some code. The first thing I wrote was the transitions. I knew galleries needed transitions and I knew they must be polymorphic so I made a simple interface and a concrete transition

package com.fg.transitions
{
import flash.display.DisplayObject;

public interface ITransition
{
function transitionTo(to:DisplayObject, from:DisplayObject=null):void;

}
}


package com.fg.transitions
{
import fl.transitions.Tween;
import fl.transitions.easing.*;
import fl.transitions.TweenEvent;

import flash.events.Event;
import flash.events.EventDispatcher;
import flash.display.DisplayObject;
import flash.display.Sprite;


public class CrossFade extends Sprite implements ITransition
{
private var tweenTo:Tween;
private var tweenFrom:Tween;


public function CrossFade()
{
super();
}

public function transitionTo(to:DisplayObject, from:DisplayObject=null):void
{
if (from) addChild(from);
addChild(to);

if (tweenFrom) tweenFrom.stop();
if (tweenTo) tweenTo.stop();

if (from)
{
from.alpha = 1;
tweenFrom = new Tween(from, "alpha", None.easeNone, from.alpha, 0, .5, true);
}
to.alpha = 0;
tweenTo = new Tween(to, "alpha", None.easeNone, to.alpha, 1, .5, true);
tweenTo.addEventListener(TweenEvent.MOTION_FINISH, motionFinishHandler, false, 0, true);
}

private function motionFinishHandler(e:TweenEvent):void
{
dispatchEvent(e.clone());
}
}
}


the next thing I made was my AbstractGallery

package com.fg.galleries
{
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.display.DisplayObject;
import flash.display.MovieClip;
import flash.display.Loader;
import flash.net.URLRequest;

import com.fg.transitions.ITransition;


public class AbstractGallery extends MovieClip
{
private var transition:ITransition;
private var imageList:Array;

private var loaders:Array/*Loaders*/;
private var loadCount:int = 0;
private var previous:DisplayObject;
private var current:DisplayObject;


public function AbstractGallery()
{
super();
loaders = [];
transition = createTransition();
imageList = createImageList();
var loader:Loader = loadImage(loadCount++);
loader.contentLoaderInfo.addEventListener(Event.CO MPLETE, firstImageCompleteHandler, false, 0, true);
}

/*abstract*/ protected function createTransition():ITransition
{
return null;
}

/*abstract*/ protected function createImageList():Array/*String*/
{
return null;
}

final protected function requestImage(location:int):void
{
if (loaders[location])
{
previous = current;
current = loaders[location];
transition.transitionTo(current, previous);
}
else
{
trace("AbstractGallery/requestImage at " + location + " has not loaded yet");
}
}

private function loadImage(place:int):Loader
{
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.CO MPLETE, loaderCompleteHandler, false, 0, true);
loader.load( new URLRequest(imageList[place]) );
loaders[place] = loader;
return loader;
}

private function loaderCompleteHandler(e:Event):void
{
if (loadCount < imageList.length) loadImage(loadCount++);
}

private function firstImageCompleteHandler(e:Event):void
{
requestImage(0);
}
}
}

keep in mind I had my "features list" of what my gallery was going to be before I started coding. I said my galleries had images loaded dynamically, transition effects, and the ability to move between images. That was pretty much it for this simple example, so that is what I abstracted into the methods. I like to comment my "abstract methods" in a similar fashion the way a Java programmer would mark the method as abstract. Since I can't do that in AS3.0 I settle for comments. So my two factory methods are createTransition() and createImageList(). In my subclasses I will override these and supply a concrete instance. The other method available to the subclasses is for changing the image. Because images loaded async i wanted that behavior to be hidden from the subclasses, so I marked the method as final protected to allow the subclasses to change images by calling that method not having to worry about if it is loaded or not. (Note: the internals of this method are not well thought out. If I were doing it for real and not as an after-thought as it isn't a factory method I would take better care of the async nature of loaded images).

Okay, so the last thing I wrote was my concrete SimpleGallery

package com.fg.galleries
{
import flash.display.MovieClip;
import flash.events.MouseEvent;

import com.fg.transitions.ITransition;
import com.fg.transitions.CrossFade;

public class SimpleGallery extends AbstractGallery
{
private var currentIndex:int=0;

public function SimpleGallery()
{
super();
next_mc.addEventListener(MouseEvent.CLICK, nextClickHandler, false, 0, true);
back_mc.addEventListener(MouseEvent.CLICK, backClickHandler, false, 0, true);
}

override protected function createTransition():ITransition
{
return images_mc.addChild(new CrossFade()) as ITransition;
}

override protected function createImageList():Array/*String*/
{
return [
"http://images.askmen.com/galleries/actress/hilary-swank/pictures/hilary-swank-picture-1.jpg",
"http://movies.popcrunch.com/wp-content/uploads/2008/02/hilary-swank.jpg",
"http://www.celebrityrolemodel.com/wp-content/uploads/2007/10/hilary_swank.jpg"
];
}

private function nextClickHandler(e:MouseEvent):void
{
currentIndex = (currentIndex+1)%3;
requestImage(currentIndex);
}

private function backClickHandler(e:MouseEvent):void
{
currentIndex = (currentIndex+3-1)%3;
requestImage(currentIndex);
}
}
}

and I was done. And if wanted to write more galleries, it would be a breeze now that I've got an abstract class that takes care of all my dirty work.

So keep in mind anytime you are doing the same process over and over again or you want to ensure consistency among subclasses, consider using the Factory Method.

THANKS FACTORY METHOD!

Jesse
05-27-2009, 12:28 PM
Nice post mate. Ever considered making it a Tutorial as well? I see you've posted a few examples of this nature and the Best Practices Articles area could always use more contributions :)

Flash Gordon
05-27-2009, 06:36 PM
Thanks :)

Yeah, I'll probably write a couple more here in the series and then make them up as tutorials. And hopefully I can get a couple others to write some stuff as well like michael and "wxvwvxx"