Home > Abstract Classes, Beginner's Guide, Factory Method, Learning Design Patterns > Beginning ActionScript 3.0 Abstraction : The Factory Method at Work

Beginning ActionScript 3.0 Abstraction : The Factory Method at Work

abstractionJoin the Campaign to Stamp Out Ugly!

Gentle Readers: When you click the Play button in this post, the UI that appears isn’t very pretty. That’s because I have no graphic design skills. By changing the requests in the Client class, you could easily make a much nicer UI. Why not give it a shot and send us your revised Client request set? You can even add new Concrete Creators and Products. Send in your refinements to our comment section. Either that or click Play and see Ugly!

The Concreteness of Abstraction

The first time I went to London, I picked up a Tube Map (Map of the London Underground) and was able to get where I wanted to go. The map of London’s tube is a masterpiece of clarity and abstraction. Based on electrical circuitry, it’s very easy to find one’s way around London even on a first visit. Likewise, Paris’ Metro has an almost identical type of map for it’s underground system, and it too is easy to find where you want to go. The secret is having just enough detail to use the system and not so much detail to make its use confusing. Further the coding is clear: different colors depict different named lines. Bakerloo is brown, Circle is yellow, Central is red and District is green. At a glance you see that arriving at Heathrow Airport, you take the Piccadilly line into town (purple) and switch lines wherever you see a white circle off the Piccadilly line. The details are not provided, and the Thames river does not run along the neat geometrical angles shown on the Tube Map. It’s just a reference point and lets you know whether you’re north or south of the Thames. The river’s or tube’s exact shape doesn’t matter; the whole thing is an abstraction of London’s subway system and a major feature of the city. The details would just get in the way of clarity.

Abstraction in OOP and Design Patterns works the same way. An object only exposes as much information as the client (requesting object) requires. The perfect abstract structure in a design pattern is an Interface because it is nothing but abstract methods. The only way to really appreciate abstraction is to see it in action; so this post concentrates on demonstrating abstractions in a fundamental design pattern—the Factory Method.

Two Interfaces and some Concrete Classes

The Factory Method requires a factory interface called Creator and a “product” interface called Product. The Creator interface provides a factory method function to create concrete instances of products through the abstract Product interface. In this implementation of the Factory Method Design Pattern, both abstract participants are interfaces (Creator and Product). Figure 1 shows the relationship between the participants in the design pattern:

<em><strong>Figure 1: </strong> Factory Method Design Pattern</em>

Figure 1: Factory Method Design Pattern

In a step-through of the pattern you see the following:

  1. Client wants a certain product. Makes request through the Creator (factory)
  2. Concrete creator specifies the concrete product and instantiates an instance, returning it to the Client

It’s a very straightforward pattern, but often beginners will puzzle over why all the work simply to get a class instance? The answer to that question lies more in the larger implementations and re-use of the pattern than it does in the small example used here. However, a good general answer is that it loosely binds the product to the request. If you change the product’s contents, you should not have to change anything else in the pattern. The Client makes the request through an abstraction and really doesn’t care about the details of the product. If the product is changed, the exact same request brings up a different object. However, nothing has to be changed to access the new object. Use the buttons below to download and play the example:
kilroyplay

Chapter 2 in our book, and other posts on this blog go into detail about the Factory Method. Here the focus is on abstraction, and the Factory Method is merely used to illustrate the power of abstraction. In order to show the range of products that can be generated from the same abstract interfaces, I used both component UIs and graphic background Shape objects as products.

Using Abstractions and Programming to the Interface

One of the key principles of design pattern programming is,

Program to the Interface and not the implementation….

In order to see both how abstractions are used and how relations work in the n the Factory Method pattern, Figure 2 shows code snippets related to each of the participants (classes and relationships).

<em><strong>Figure 2: </strong> Coded Elements of the Factory Method pattern</em>

Figure 2: Coded Elements of the Factory Method pattern


In looking at Figure 2 code snippets, notice how many times that Creator or Product are used. Both reflect the wholly abstract interfaces in the program. By making references to the interfaces (or abstract classes), the binding between the Client and the requests is very loose. By having loose binding through abstractions, the developer can better update and change the program. The only part of the objects exposed are those that are required. If the concrete products change, the Client is not tightly bound to the concrete elements because the requests hold references to the abstract interfaces and not the concrete participants. (Read on to see how these are all put together!)

The Client, Creator and Product

This section shows the code for all of the participants. (You can download it all if you’d like to follow this post using Flash or Flash Builder by clicking the Download button above.) I’d like to start with the Client and then examine the abstract interfaces. We’ll start with the Client:

?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 bkdp:Creator=new MakeBackdrop();
		private var btn1:Creator=new MakeButton();
		private var btn2:Creator=new MakeButton();
		private var txtA:Creator=new MakeTextArea();
		private var txtI:Creator=new MakeTextInput();
 
		public function Client()
		{
			addChild(bkdp.factoryMethod(12,15,"0xcc0000"));
			addChild(btn1.factoryMethod(20,30,"Top"));
			addChild(btn2.factoryMethod(20,60,"Bottom"));
			addChild(txtA.factoryMethod(130,30,"Lots of text can go here!"));
			addChild(txtI.factoryMethod(20,100,"Input here"));
		}
	}
}

In looking at the Client, you can see that all requests are through the Creator interface. Because all of the concrete creators (factories) implement the Creator, the objects can be typed to the interface and not the concrete factories. Note that while the Client is requesting concrete Products, products are not even mentioned once. The factoryMethod() takes care of making the request that delivers the product. Think of it as requesting a cup of coffee from a barista. You put in your order for a Café Latte Uruguay Negro (my coffee house is more pretentious than yours). You don’t care about how the barista makes the cup of coffee you ordered; you just know to ask him for the specific product you want, and he’ll take care of it. The Client is doing the same thing.

The Creator (factory) interface is simple because it is abstract. You provide the framework, and the implementations will take care of the concrete elements.

?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
package
{
	import flash.display.Sprite;
	interface Creator
	{
		function factoryMethod(ex:Number,why:Number,lbl:String):Sprite;
	}
}

As you will see in the concrete implementations of the Creator, you can do a lot of different things with the three parameters.

Next, the Product interface looks very similar to the Creator. In fact, other than different names for the interface and method, they’re the same. The don’t have to be, but in this case they just happen to be. However, as you will see, the implementations of each are different individually and as a group.s

?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
package
{
	import flash.display.Sprite;
 
	interface Product
	{
		function uiProduct(ex:Number,why:Number,lbl:String):Sprite;
	}
}

Going back to the example of the London Tube Map, you can see that the abstractions have very little detail. One “sounds like” it makes something (factoryMethod()) and the other “sounds like” a product (uiProduct()). However, you have enough information to know that each does something different.

The Concrete Implementations: Factories and Products

The final step in working with the abstractions in the interfaces is to make them do something concrete. First, we’ll deal with the factories. The following code creates factories for each of the products:

?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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
//Make buttons
package
{
	import flash.display.Sprite;
 
	public class MakeButton implements Creator
	{
		private var btn:Product = new UiButton();
		public function factoryMethod(ex:Number,why:Number,lbl:String):Sprite
		{
			return btn.uiProduct(ex,why,lbl);
		}
	}
}
 
//Make backdrop
package
{
	import flash.display.Sprite;
 
	public class MakeBackdrop implements Creator
	{
		private var bkDrop:Product = new UiBackdrop();
		public function factoryMethod(ex:Number,why:Number,lbl:String):Sprite
		{
			return bkDrop.uiProduct(ex,why,lbl);
		}
	}
}
 
//Make TextAreas
package
{
	import flash.display.Sprite;
 
	public class MakeTextArea implements Creator
	{
		private var txtArea:Product = new UiTxtArea();
		public function factoryMethod(ex:Number,why:Number,lbl:String):Sprite
		{
			return txtArea.uiProduct(ex,why,lbl);
		}
	}
}
 
//Make TextInput
package
{
	import flash.display.Sprite;
 
	public class MakeTextInput implements Creator
	{
		private var txtInput:Product = new UiTxtInput();
		public function factoryMethod(ex:Number,why:Number,lbl:String):Sprite
		{
			return txtInput.uiProduct(ex,why,lbl);
		}
	}
}

As you can see, they’re all pretty much the same. They declare instances as a Product but instantiate them as concrete product implementations. There’s not a lot of variety; just the name of the instantiated concrete product.

The concrete products show a great deal of variation, though. This demonstrates the flexibility of abstractions and the loose bindings between a client object and the requested products.

?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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
//Button
package
{
	import fl.controls.Button;
	import flash.display.Sprite;
	import flash.events.MouseEvent;
 
	public class UiButton implements Product
	{
		private var btnPack:Sprite=new Sprite();
		private var button:Button=new Button();
 
		public function uiProduct(ex:Number,why:Number,lbl:String):Sprite
		{
			button.label = lbl;
			button.addEventListener(MouseEvent.CLICK,btnBusiness);
			button.x = ex;
			button.y = why;
			btnPack.addChild(button);
			return btnPack;
		}
 
		private function btnBusiness(e:MouseEvent):void
		{
			trace("Whatever business you have in mind....");
		}
	}
}
 
//Backdrop
package
{
	import flash.display.Sprite;
	import flash.display.Shape;
 
	public class UiBackdrop implements Product
	{
		private var backdropHolder:Sprite=new Sprite();
		private var backdrop:Shape=new Shape();
 
		public function uiProduct(ex:Number,why:Number,lbl:String):Sprite
		{
			backdrop.graphics.beginFill(int(lbl));
			backdrop.graphics.lineStyle(0,0x0000cc);
			backdrop.graphics.drawRect(ex, why, ex+100, why+100);
			backdrop.graphics.endFill();
			backdropHolder.addChild(backdrop);
			return backdropHolder;
		}
	}
}
 
//Text Area
package
{
	import fl.controls.TextArea;
	import flash.display.Sprite;
 
	public class UiTxtArea implements Product
	{
		private var taPack:Sprite=new Sprite();
		private var txtArea:TextArea=new TextArea();
 
		public function uiProduct(ex:Number,why:Number,lbl:String):Sprite
		{
			txtArea.text = lbl;
			txtArea.width = 200,txtArea.height = 200;
			txtArea.x = ex;
			txtArea.y = why;
			taPack.addChild(txtArea);
			return taPack;
		}
	}
}
 
//Text Input
package
{
	import flash.display.MovieClip;
	import fl.controls.TextInput;
	import flash.display.Sprite;
 
	public class UiTxtInput implements Product
	{
		private var tiPack:Sprite=new Sprite();
		private var txtInput:TextInput=new TextInput();
 
		public function uiProduct(ex:Number,why:Number,lbl:String):Sprite
		{
			txtInput.text = lbl;
			txtInput.x = ex;
			txtInput.y = why;
			tiPack.addChild(txtInput);
			return tiPack;
		}
	}
}

As you can see in the different implementations of the Product object, there’s an immense variation. The Factory Method uses abstraction to make it very easy for the Client to request what’s needed, provide loose binding, and do it while allowing enough room for variation to do whatever the developer wants.

The best part comes when I decide to change something. Suppose I decided I want to make my own TextArea object using a TextField, TextFormat and other coded objects so I don’t have to worry too much about component differences between Flash Professional and Flash Builder. I can simply change my implementation in the concrete product without affecting anything else. In other words, I can make huge programs without having to start all over from scratch.

Thanks abstraction!

Share

Related posts:

  1. Reuse Where Objects Change: Factory Method at Work with Lazy Instantiation
  2. ActionScript 3.0 Lazy Initialization and the Factory Method Design Pattern
  3. Friends with Benefits: State and Factory Method Together at Last—Part II
  1. Mark A.
    May 31, 2011 at 9:43 am | #1

    Thank you Bill for all the afford you effort you put into this! Much appreciated

  2. June 1, 2011 at 6:44 am | #3

    Just wanted to say that I think you put alot of effort in writing this post. Most informative and appreciated.

    Ryan

    • June 1, 2011 at 1:16 pm | #4

      Hi Ryan,

      Thanks for that. Give us a hand making it look pretty!

      Kindest regards,
      Biill

  3. June 9, 2011 at 5:58 am | #5

    I know I struggled with the Factory pattern because I kept seeing ‘variations’ which I later learned were termed “Simple Factory”, “Factory” and “Abstract Factory”. I summed them up here using the analogy of songs and cover-bands. hopefully this will help people to cement what they have learned here

    Note: The following url links to the two earlier posts and should ideally be read in order.
    http://nwebb.co.uk/blog/?p=91

    • June 11, 2011 at 7:33 am | #6

      Hi Neil,

      That is a great example of an Abstract Factory! Your example is quite clear and perfectly illuminates the purpose and use of the Abstract Factory design pattern. Nice work.

      Kindest regards,
      Bill

  4. June 13, 2011 at 12:35 am | #7

    thanks Bill. Much appreciated :)

  1. No trackbacks yet.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>