Hitchhiker’s Guide to ActionScript 3.0: The Dragon Factory—Part 2

Take This One to Work

Take This One to Work

You will remember the initial query that launched this two-part post started with an inquiry into the possibility of dragging a Video while it was playing. In order to make the query more relevant to design patterns, this second part looks at the process for expanding the larger issue of dragging objects inside a Sprite. Whenever undertaking such a project, begin with the Variation Table (aka Magic Table) to find the possible variations. The wrong way to start developing a design pattern for a practical problem is to look at all of the different class diagrams and pick one that looks like it will fit your problem. Instead, always start by asking, what varies?
download
In this case, the “hitchhiker” to be dragged is the particular type of object in question. We saw how to drag a Video object, but what about other objects that need to hitchhike on a Sprite to be dragged? So, we’re talking about different objects—or put otherwise—object variation. The closest matching variation in the Magic Table is the variation of the subclass of the object that is instantiated. That means a Factory Method design. (See how easy that was?)

Next, I opened up the Design Pattern Catalog (which still needs to be finished) but includes all of the Creational Patterns. It provided the class diagram and other information about the Factory Method that I needed to get started on the project. Also, quite by chance the Design Pattern Catalog was developed using the Factory Method as well—in fact it uses two.

The Dragon Factory

One of the aids I’ve been using to develop design pattern projects is to create a class diagram made up of the files I’ll be using. This helps to give me an ongoing overview, and even though the files are devoid of content initially, they serve as a development map. Also, I like to start with the minimum number of elements needed. So I’ll need at least two different objects, or Product implementations. Since I’ve already got the Video, I’ll use it, and I’ll use the Shape object for the second hitchhiker object. Figure 1 shows the File Class Diagram for this project:

Figure 1: Dragon Factory File Class Diagram

Figure 1: Dragon Factory File Class Diagram

In considering any Factory Method design pattern, GoF notes that you can select from two varieties. One variety is where the Creator is an abstract class and you subclass concrete creators as factory implementations . The other variety is to have a single concrete Creator class that serves as a big factory. (During the Soviet Era in the Russia, they had a huge truck factory the size of a small city that produced huge trucks in great quantities—sort of like the single concrete Creator class.) I opted for the abstract creator to keep the design looser. With a single Creator class I would have had to used conditional statements to farm out out the different types of objects, and as you will see I was able to keep the design if-free.

The Product Classes

The design called for two different objects, but I wanted to leave the door open for further objects that I could include in the design at a later date. Thus, the Product participant in the design pattern needed to be abstract, and so I used an abstract class. It set up one abstract method, a concrete property, and two concrete methods for dragging. The concrete property (named sled) is employed for giving the non-Sprite objects a ride. The abstract method provides flexibility in implementation for different objects. Given the difference between a Video object, including a Camera instance, and a Shape, the design needed that flexibility.

The following three listings show the abstract Product class (DragProduct) and the two implementations (DragVid and DragShape).

?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package 
{
	//Abstract class--serves as interface
	import flash.display.Sprite;
	import flash.events.MouseEvent;
	import flash.errors.IllegalOperationError;
 
	public class DragProduct extends Sprite
	{
		protected var sled:Sprite=new Sprite();
 
		// ABSTRACT Method (must be overridden in a subclass)
		protected function setElements():void
		{
			throw new IllegalOperationError("Abstract method: must be overridden in a subclass");
		}
 
		protected function doDrag(e:MouseEvent):void
		{
			this.startDrag();
		}
 
		protected function unDrag(e:MouseEvent):void
		{
			this.stopDrag();
		}
	}
}
?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package 
{
	//Concrete Product
	import flash.display.Sprite;
	import flash.media.Camera;
	import flash.media.Video;
	import flash.events.MouseEvent;
 
	public class DragVid extends DragProduct
	{
		private const WIDE:uint=320;
		private const HIGH:uint=240;
		private var vid:Video = new Video(WIDE,HIGH);
		private var cam:Camera = Camera.getCamera();
 
		public function DragVid()
		{
			setElements();
			sled.addEventListener(MouseEvent.MOUSE_DOWN,doDrag);
			sled.addEventListener(MouseEvent.MOUSE_UP,unDrag);
		}
 
		override protected function setElements():void
		{
			cam.setMode(WIDE,HIGH,20);
			cam.setQuality(0,100);
			vid.smoothing=true;
			vid.attachCamera(cam);
			vid.x = 100,vid.y = 100;
			addChild(sled);
			sled.addChild(vid);
		}
	}
}
?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package 
{
	//Concrete Product
	import flash.display.Sprite;
	import flash.display.Shape;
	import flash.events.MouseEvent;
 
	public class DragShape extends DragProduct
	{
		private var shape:Shape = new Shape();
 
		public function DragShape()
		{
			setElements();
			sled.addEventListener(MouseEvent.MOUSE_DOWN,doDrag);
			sled.addEventListener(MouseEvent.MOUSE_UP,unDrag);
		}
 
		override protected function setElements():void
		{
			shape.graphics.beginFill(0x009900);
			shape.graphics.drawEllipse(450, 10, 50, 50);
			addChild(sled);
			sled.addChild(shape);
		}
	}
}

In the two Product child classes (DragVid and DragShape) you can clearly see where the variation is in the subclass of object that is instantiated just like it shows in the Magic Table.

The Factory Classes

As noted above, I opted for the abstract Creator and relied on specific implementations for the factories that would individually create the different draggable objects. The abstract class that makes up the interface containes a single Sprite property and two methods; one public and concrete and the other, protected and abstract. The public method serves as a getter and the protected method returns the finished product. The following three listings show the Creator classes:

?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package
{
	//Asbstract class--serves as interface
	//Creator
	import flash.display.Sprite;
	import flash.errors.IllegalOperationError;
 
	public class Creator
	{
		private var dragNow:Sprite;
 
		public function createDragable():Sprite
		{
			dragNow = getDragon();
			return dragNow;
		}
 
		// ABSTRACT Method (must be overridden in a subclass)
		protected function getDragon():Sprite
		{
			throw new IllegalOperationError("Abstract method: must be overridden in a subclass");
			return null;
		}
	}
}
?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package 
{
	//Concrete Factory
	import flash.display.Sprite;
	import flash.errors.IllegalOperationError;
 
	public class VidCreator extends Creator
	{
		override public function createDragable():Sprite
		{
			return(new DragVid());
		}
	}
}
?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package 
{
	//Concrete Factory
	import flash.display.Sprite;
	import flash.errors.IllegalOperationError;
 
	public class ShapeCreator extends Creator
	{
		override public function createDragable():Sprite
		{
			return(new DragShape());
		}
	}
}

tightprogrammerThe Creator implementations simply call for either a DragVid or DragShape instance. You may be thinking, you could have used a single Creator implementation (concrete factory) to call for one or the other with a simple conditional statement. However, I try to avoid conditional statements and keep the design loose. Why tighten up your design when you don’t have to? (You’re not going to run out of silicon. )

The Lazy Client

I’ve decided that I don’t want the client doing too much. After all, the Client’s job is to make requests; not to create things from scratch. It’s here to eat the cake; not to bake it. As a result, the client we have for this little project is pretty simple as you can see in the following listing:

?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package 
{
	import flash.display.Sprite;
 
	public class Client extends Sprite
	{
		private var dragThing:Creator=new VidCreator();
		private var dragThing2:Creator=new ShapeCreator();
		private var rider:Sprite;
		private var rider2:Sprite;
 
		public function Client()
		{
			rider=dragThing.createDragable();
			addChild(rider);
 
			rider2=dragThing2.createDragable();
			addChild(rider2);
 
		}
	}
}

The Client implements each draggable object with just a few lines of code. It uses a Creator typed object to instantiate concrete factories. Then it uses the factories to call a method that cranks out the desired draggable products. Figure 2 shows what you can expect to see—as long as you have your camera hooked up.

<em><strong>Figure 2:</strong> Easily Amused Subject Contemplating Draggable Green Ball</em>

Figure 2: Easily Amused Subject Contemplating Draggable Green Ball

If you’d like to give it a run, hook up your camera and click the “Play Button”:
play

Try dragging both objects—they drag just fine.

Why Take This to Work?

You might be thinking,

This doesn’t look useful…

It’s funny, the same thing was said about the electric motor and the Internet when they first came about. (It may have been packet switching, but you get the idea…) In their book, Head First Design Patterns, the Freemans have these little cut out pieces of paper you can use to help put different parts of design patterns and programs together. You’re supposed to move them around in different configurations to help you understand design patterns programming. You could do the same thing with a TextField probably. Oh, but I forgot! You can’t drag a TextField object. What was I thinking?

4 Responses to “Hitchhiker’s Guide to ActionScript 3.0: The Dragon Factory—Part 2”


  • Hello Bill,
    Thanks for this post. I am trying to learn design patterns but it is a slow process with a brain like porridge.

    I tend to develop small e-learning interactions and sometimes struggle to see how DP’s are relevant to my work but this post gave me something to ‘hook’ into and ‘take to work’.

    On the odd occasion I do build something more complex, I seem to have adopted the learning philosophy of make as many mistakes as one coder possibly can, then slowly learn from them.

    Switching from AS2 to AS3 has helped to curtail some of my more, er, ‘unique’ coding practices but my gut tells me that design patterns will help me work smarter / have more time for beer.

    So, thanks again and I look forward to future posts.

    Cheers,
    Patrick

  • Hi Patrick,

    The thing about design patterns that is most important for me is that they help me rid myself of some pretty bad practices. Also, I’ve learned to be patient with both design patterns and myself. When I’ve learned other aspects of coding, the syntax, key statements and structure have come very fast. However, design patterns rarely come fast, and they’re difficult. Okay, so they’ll take a bit longer, but there’s no hurry. Just poke here and poke there, and pretty soon you’ve got a set of better coding practices that follow the principles that have developed over the years in computer science and related fields. As the practices improve, you’ll start seeing your code take a different direction.

    Your point about developing small e-learning interactions is probably true with a lot of people using Flash and Flex—it has been with me as well. Actually, the small projects make it a bit easier to try out some different design patterns. Tackling a big project with a complex system of coding may actually take longer, and the only thing you get sooner is the realization that design patterns are handy little critters.

    Kindest regards,
    Bill

  • Interesting article. I’m completely self taught in several programming languages and have picked up my knowledge here and there from numerous places. Therefore, I haven’t learned a lot about common industrial standards such as design patters etc. (I’m going through a book on some right now, but it’s rather slow reading :(

    The funny thing is, I have an intense love for logic, and often I get gut feelings about what is a good idea or bad idea, even though I can give no clear reason, (such as the “prototype” property, just seems like a really bad idea, still not sure why) and often I find that I have been following certain design patterns without knowing.

    I still don’t understand exactly why you used the Factory Method. I have an idea, but I’m not sure if it is an existing design pattern or just some unstructured code, or the advantages and disadvantages to using my idea compared to the Factory Method.

    I was considering taking a different approach, creating your own dragging system. Since all DisplayObjects, weather Sprites or not, can listen for mouse events, it would be a lot easier to pick up on that.

    Clients would create new “DragListener” instances. A DragListener listens for MOUSE_DOWN and MOUSE_UP parameters from one passed in object, and if the bounds are within a passed set area (likely a reference to a Rectangle, or can even be references to a Shape object or bitmap, with individual pixels tested using hitTest, or a custom hitArea object), the DragListener instance will dispatch “DragEvents”.

    ?View Code ACTIONSCRIPT
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    
    class DragListener extends EventDispatcher
    {
       public function DragListener(mouseEventDispatcher:IEventDispatcher, hitArea:HitArea)
       {
          mouseEventDispatcher.addEventListener(MouseEvent.MOUSE_DOWN, startDragDetection);
       }
     
       public property mouseEventDispatcher:IEventDispatcher;
       public property hitArea:HitArea;
     
       private var startX:Number;
       private var startY:Number;
     
       function startDragDetection(mouseEv:MouseEvent)
       {
          if (hitArea.testPoint(mouseEv.location))
          {
             mouseEventDispatcher.addEventListener(MouseEvent.MOUSE_MOVE, moveDetection);
             mouseEventDispatcher.addEventListener(MouseEvent.MOUSE_UP, stopDragDetection);
             mouseEventDispatcher.removeEventListener(MouseEvent.MOUSE_DOW, startDragDetection);
     
             startX = mouseEv.localX;
             startY = mouseEv.localY;
     
             this.dispatchEvent(new DragEvent(DragEvent.START, startX, startY, 0, 0));
          }
       }
     
       function moveDetection(mouseEv:MouseEvent)
       {
          this.dispatchEvent(new DragEvent(DragEvent.MOVE, startX, startY, mouseEv.localX, mouseEv.localY));
       }
     
       function stopDragDetection(mouseEv:MouseEvent)
       {
          mouseEventDispatcher.removeEventListener(MouseEvent.MOUSE_MOVE,
    moveDetection);
          mouseEventDispatcher.removeEventListener(MouseEvent.MOUSE_UP, stopDragDetection);
          mouseEventDispatcher.addEventListener(MouseEvent.MOUSE_DOWN, startDragDetection);
     
          this.dispatchEvent(new DragEvent(DragEvent.STOP, startX, startY, mouseEv.localX, mouseEv.localY));
       }
     
    }

    Here is pseudo code for how the DragEvent could look, ignoring non-essential or irrelevant properties and code:

    ?View Code ACTIONSCRIPT
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    
     
    class DragEvent extends Event
    {
       public static const START;
       public static const MOVE;
       public static const STOP;
     
       public function DragEvent(eventString:String, startX, startY, currentX,
    currentY)
     
       public readonly property currentTarget;
       public readonly property eventString;
     
       public readonly property startX;
       public readonly property startY;
       public readonly property currentX;
       public readonly property currentY;
     
       //The x and y values last dispatched - for simplicity
       public readonly property lastX;
       public readonly property lastY;
     
       //Yes, a long name, I know. Yet, descriptive
       public readonly property offsetSinceLastX;
       public readonly prpperty offsetSinceLastY;
     
       public readonly property offsetX
          {  return currentX - startX; }
       public readonly property offsetY;
          {  return currentY - startY; }
     
    } // END DragEvent -----------------------------------------
    -----

    Finally, here is some sample code:

    ?View Code ACTIONSCRIPT
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    
     
     
       //None of the below are really display objects or can listen for mouse
    events
       var line1:Line;
       var circle1:Circle;
       var rectangle1:Rectangle; //Real AS3 class, but not a display object
       var movie1:Video; //This is a display object, but has no "startDrag" function
       var sprite1:Sprite //This has a startDrag function, but we won't be using
    it
     
       //For the line, we have told it to only move once the mouse
       //has come to a complete stop in the dragging
       var lineDragger:DragListener = new DragListener(stage, line.hitArea);
       lineDragger.addEventListener(DragEvent.STOP, onStop);
       function onStop(dragEv:DragEvent)
       {
          line1.x += dragEv.offsetX;
          line1.y += dragEv.offsetY;
     
          //Redraw the line now
          this.graphics.clear();
          this.graphics.drawLine(line1);
       }
     
       //Listen on the entire stage, and drag the circle and
       //rectangle at the same time and just as much
       //updating each frame
       var shapeDragger = new DragListener(stage, stage);
       var rectStartX, rectStartY;
       var circStartX, circStartY;
     
       shapeDragger.addEventListener(DragEvent.START, onStart);
       function onStart(dragEv:DragEvent)
       { //Set the start X and Y values for the circle and rectangle  }
       shapeDragger.addEventListener(DragEvent.MOVE, moveShapes);
       function moveShapes(dragEv:DragEvent)
       {
          circle.x = circStartX + dragEv.offsetX;
          //etc...
       }
     
       //You get the point...
     
    }

    These events are more of suggestions for the target object to move to a certain area, and don’t really need to be followed. Objects can choose weather or not to do anything in the listener functions.

    This will allow even objects that are not DisplayObjects (such as lines drawn with the Graphics class) to be dragged as well. In addition, only one display object can be dragged with startDrag at a time, while many objects can be dragged with either several instances, or one instance dragging several objects.

    I’m not sure if my thinking is way off the road from Design Patterns…

    There would be a bit of a difference for different coordinate spaces, and not all objects may have a “mouseX” and “mouseY” property, but a third parameter or a separate property could easily contain a simple system which points to which property names to look for these coordinates.

    Also, do you have any compiled list of popular design patters with perhaps some AS3 example? It would be really handy if you extended the “variation table” with a feature that allows you to double click on an item for examples and more information.

    Looking forward to reading the rest of your posts,
    Andreas

  • Hi Andreas,

    Thanks for your patience in my getting back to you. Your query about a better way to drag objects is perfectly reasonable, but probably outside of the realm of design patterns in this sense—it concerns comparative algorithms with different syntaxes. Design patterns in computing are really about larger issues, and the level of abstraction is one that asks questions about re-usability and maintenance of programs with multiple classes.

    However, your question about the Factory Method is pertinent to the issues we’re pondering on this blog. The Factory Method is one of the essential design patterns (see Chapter 2 in our book) and in several places on this blog. It is important because it helps to loosen up a program. By separating the creation of an object (in a ‘factory’ class identified as a creation participant) from its use by a Client class, you are able to loosen the relationship between creation and use. In this way, it’s a lot easier to change and update a program because you don’t have to worry about making changes that will slither through your program and disrupt one part with the added materials.

    As for a list of popular design patterns with examples in AS3–that’s what our book and this blog is all about. It’s full of examples. Enjoy!

    Kindest regards,
    Bill

Leave a Reply