The Case of the Crowded Client
After I made my video player that I planned to use to illustrate refactoring a non-design pattern program into a design pattern one, I noticed how crowded the Client class had become. Most of the crowding was caused by creating and populating UIs and event handling functions. One class that I use a lot to populate UI components is the DataProvider. As I went through and edited and organized my videos from a vacation to Prague, CZ, the DataProvider kept growing. Using MP4 files converted into F4V files, I used very similar formats for the data portion of all of the DataProvider instance. Why not put the DataProvider data into a separate class? Then, I could just call an instance of the class with the data and not have to clutter the Client class with messy DataProvider information.
Lazy Programming can be a Lot of Work
After moving the DataProvider data to another class, I realized that I had two other projects using the video player that accessed the same kind of data. So, while I was at it, I might as well add classes for these other projects. In fact, why not add a common interface as well? Then I could program all data requests to the interface instead of the implementation. Further, I could add some separation between the DataProvider items and the Client requests by adding a factory, and if I were going to do that, I might as well go ahead and create a Factory Method design pattern. Then, whenever I wanted to add a class with data for different projects I could do so without having to worry about messing up the rest of the program. Each project was handled using a Concrete Product class.
The Big Picture
To begin with a clear idea of what is going on in this refactoring exercise, you’ll need to get the big picture. Figure 1 provides an overview of how the video player is to be refactored using two design patterns:

The video player is essentially a state machine, and in Part II of this post, we’ll look at how we’ll refactor the hack-job player into a state player.
Getting and Using the Data
Using a List component with ActionScript 3.0 code requires that a DataProvider object be assigned as a data source. The DataProvider object can be loaded with data in several ways—using the addItem() method, an array or XML files. For this project, I’ll be using the addItem() method, but you can substitute the other data loading techniques if you wish.
The DataProvider object requires two elements for this project. The first element is a label for the List component and the second is the URL to the video. The label element requires nothing more than a label indicating what video plays. Using Flash Media Interactive Server 3.5.2 (FMIS) and H. 264 format requires the following kind of reference:
mp4: folderName/fileName.f4v
If you’re not familiar with Flash Media Server, no problem. This system works with a simple progressive download as well. So if you’d rather work with video files from a Web server, you can change the reference to:
folderName/fileName.flv
(You cannot use F4V (H.264) files with progressive download).
So, the basic data-loading procedure is:
dataProvider.addItem( { label: "LabelName", data:" mp4: folderName/fileName.f4v " } );
In this first post, all I want to do is to look to see how the DataProvider is moved out of the Client and into a Factory Method design pattern.
Getting Data From the DataProvider Object
Initially, the Client contained the code for all operations involving the DataProvider. The List instance (list) was then assigned the DataProvider instance. The following shows code snippet the relevant features in the Client
1 2 3 4 5 6 | var prague:DataProvider=new DataProvider(); prague.addItem( { label: "Panorama", data:"mp4:praguevid/panorama.f4v" } ); prague.addItem( { label: "Charles Bridge", data:"mp4:praguevid/charlesbridge.f4v" } ); prague.addItem( { label: "Old & New Town", data:"mp4:praguevid/newtown.f4v" } ); prague.addItem( { label: "Mucha Glass", data:"mp4:praguevid/mucha.f4v" } ); list.dataProvider = prague; |
Once the data has been added to the List object, it is retrieved when an item in the list is clicked. The line,
list.addEventListener(ListEvent.ITEM_CLICK,startPlay);
triggers a function used to get the URL for the video and play the selected video. The following operation shows how the selection and play events are handled:
1 2 3 4 5 6 7 8 9 10 | private function startPlay(e:ListEvent):void { if (ns) { //f4vid is a private string variable f4vid = e.item.data; vid.attachNetStream(ns); ns.play(f4vid); } } |
The key connection in the function is assigning the value of e.item.data to the string variable (f4vid). The data are used as the URL to play the selected video. That operation is fine the way it is and belongs in the Client as a request. What needs to be removed from the Client is the operation that populates the DataProvider.
Building a Factory Method for Populating and Returning a DataProvider: A Friend with Benefits
You might be asking yourself,
Is this really necessary? Why not just leave the DataProvider in the Client and populate it there?
The short answer is, of course not! It is not necessary, but you will find many benefits in doing so:
- Removes clutter from the Client
- Client can make request from external source
- Contents of DataProvider can be changed without breaking the rest of the program
- Other programs can use the same design pattern
- Adds flexibility for change and development
To get started, Figure 2 shows a chart diagram for this project dealing with a single concrete product—data for a series of videos.

Figure 2: Factory Method for a single set of data
In this case, the List object is in the Client class, but because it is the only object in the class that uses the data generated in the Factory Method design pattern, it is listed.
First, the Creator class provides the interface for all concrete creators. Most importantly is the factory method to be used in creating and delivering the DataProvider to the Client for use in the List component.
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 { //Abstract class //Creator import fl.data.DataProvider; import flash.errors.IllegalOperationError; public class Creator { private var dataNow:DataProvider; public function selectData():DataProvider { dataNow = createData(); return dataNow; } // ABSTRACT Method (must be overridden in a subclass) protected function createData():DataProvider { throw new IllegalOperationError("Abstract method: must be overridden in a subclass"); return null; } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | package { //Concrete Creator (Factory) import fl.data.DataProvider; import flash.errors.IllegalOperationError; public class ConcreteCreatorPrague extends Creator { private var ds:DataSupply; override protected function createData():DataProvider { ds=new Prague(); return (ds.deliverData()); } } } |
1 2 3 4 5 6 7 8 9 10 | package { import fl.data.DataProvider; //Product interface DataSupply { function deliverData():DataProvider; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | package { import fl.data.DataProvider; public class Prague implements DataSupply { public function deliverData():DataProvider { var prague:DataProvider=new DataProvider(); prague.addItem( { label: "Panorama", data:"mp4:praguevid/panorama.f4v" } ); prague.addItem( { label: "Charles Bridge", data:"mp4:praguevid/charlesbridge.f4v" } ); prague.addItem( { label: "Old & New Town", data:"mp4:praguevid/newtown.f4v" } ); prague.addItem( { label: "Mucha Glass", data:"mp4:praguevid/mucha.f4v" } ); return prague; } } } |
Finally, all that the Client needs is to request the data it needs. Because, only a single concrete product has been created, this is simply a matter of creating List and DataProvider instances. Then to populate the DataProvider with data, the Client requests the data by instantiating the desired concrete creator through the Creator interface (abstract class). The following listing shows the operations in the process:
1 2 3 4 5 6 7 | var create:Creator=new ConcreteCreatorPrague(); var prague: DataProvider=new DataProvider(); prague=create.selectData(); var list:List=new List(); list.dataProvider = prague; |
Under any circumstances you need to create a DataProvider instance, a List instance and assign a DataProvider object to the List object. Using the Factory Method pattern, the only line of code required to request the contents for the DataProvider is the following:
prague=create.selectData();
You can have hundreds of elements in the DataProvider, but using the suggested pattern, you still just need a single line in the Client to access it.
Expanding The Concrete Products Shows the Benefits
After going through the process we have, you may be thinking, What benefits?. After all, the DataProvider class itself is a pretty handy class in its own right. Why complicate things?
For a single data source, it is a bit hefty, but when you look at a larger picture, you can see that this Factory Method program can be used as a reliable mechanism to add more data sources. Figure 3 shows an expanded version of the original pattern:

Figure 3: Factory Method for expanded set of data
Before you get started on this new set of classes, you might want to download the files here. The Product interface (DataSupply) is the same as the original. However, because the concrete creator has more than a single option from the concrete products now, we’ll have to add a parameter to the factory method for choosing the concrete product we want. However, as you can see in the following program, there’s not much that is changed from the original Creator class:
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 { //Abstract class //Creator import fl.data.DataProvider; import flash.errors.IllegalOperationError; public class Creator { private var dataNow:DataProvider; public function selectData(dataSource:String):DataProvider { dataNow = createData(dataSource); return dataNow; } // ABSTRACT Method (must be overridden in a subclass) protected function createData(dataSource:String):DataProvider { throw new IllegalOperationError("Abstract method: must be overridden in a subclass"); return null; } } } |
The concrete creator needs to be changed as well, both to add the new parameter and to set up a way to choose among the concrete products. It has been renamed, ConcreteCreatorVid to reflect that it is used in dealing with data that is used with the video player:
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 | package { //Factory import fl.data.DataProvider; import flash.errors.IllegalOperationError; public class ConcreteCreatorVid extends Creator { private var ds:DataSupply; override protected function createData(dataSource:String):DataProvider { switch (dataSource) { case "prague" : ds=new Prague(); return(ds.deliverData()); break; case "graduation" : ds=new Graduation(); return(ds.deliverData()); break; case "bloomfield" : ds=new Bloomfield (); return(ds.deliverData()); break; default : throw new Error("Invalid video set specified"); return null; } } } } |
Notice that the ConcreteCreatorVid holds a reference to the product interface (DataSupply) and then instantiates the concrete products (Prague, Graduation, Bloomfield) using an object typed as an interface. The Prague concrete product class does not change; so all that’s left is to create concrete product classes for the Graduation and Bloomfield video projects.
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 | //Data for Graduation Project package { import fl.data.DataProvider; public class Graduation implements DataSupply { public function deliverData():DataProvider { var graduation:DataProvider=new DataProvider(); graduation.addItem( { label: "Gathering", data:"mp4:graduationvid/gathering.f4v" } ); graduation.addItem( { label: "Walking", data:"mp4:graduationvid/walk.f4v" } ); graduation.addItem( { label: "Awarding Degrees", data:"mp4:graduationvid/degree.f4v" } ); return graduation; } } } //Data for Bloomfield project package { import fl.data.DataProvider; public class Bloomfield implements DataSupply { public function deliverData():DataProvider { var bloomfield:DataProvider=new DataProvider(); bloomfield.addItem( { label: "Introduction", data:"mp4:bloomfieldvid/intro.f4v" } ); bloomfield.addItem( { label: "My Street", data:"mp4:bloomfieldvid/shadywalk.f4v" } ); bloomfield.addItem( { label: "History", data:"mp4:bloomfieldvid/park.f4v" } ); bloomfield.addItem( { label: "People", data:"mp4:bloomfieldvid/people.f4v" } ); return bloomfield; } } } |
At this point you have all of the Factory Method classes. So now (finally!) we can look at a Client class that makes requests. Keep in mind that this is only one of two design patterns that this project is using. (In the next installment, we’ll refactor the a non-OOP video player into a State design pattern and incorporate this data-generating Factory Method design pattern.)
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 | package { import flash.display.Sprite; import fl.events.ListEvent; import flash.events.MouseEvent; import fl.controls.Button; import fl.controls.List; import fl.data.DataProvider; public class Client extends Sprite { private var btnPrague:Button=new Button(); private var btnGrad:Button=new Button(); private var btnBloom:Button=new Button(); private var list:List=new List(); private var create:Creator=new ConcreteCreatorVid(); public function Client() { setUI(); btnPrague.addEventListener(MouseEvent.CLICK,getPrague); btnGrad.addEventListener(MouseEvent.CLICK,getGraduation); btnBloom.addEventListener(MouseEvent.CLICK,getBloomfield); } private function setUI():void { btnPrague.x = 35,btnPrague.y = 160; btnPrague.label = "Prague"; btnPrague.textField.background = true; btnPrague.textField.backgroundColor = 0xA19580; addChild(btnPrague); btnGrad.x = 35,btnGrad.y = 190; btnGrad.label = "Graduation"; btnGrad.textField.background = true; btnGrad.textField.backgroundColor = 0xA19580; addChild(btnGrad); btnBloom.x = 35,btnBloom.y = 220; btnBloom.label = "Bloomfield"; btnBloom.textField.background = true; btnBloom.textField.backgroundColor = 0xA19580; addChild(btnBloom); list.x = 35,list.y = 50; addChild(list); } private function getPrague(e:MouseEvent):void { var prague:DataProvider=new DataProvider(); prague = create.selectData("prague"); list.dataProvider = prague; } private function getGraduation(e:MouseEvent):void { var graduation:DataProvider=new DataProvider(); graduation = create.selectData("graduation"); list.dataProvider = graduation; } private function getBloomfield(e:MouseEvent):void { var bloomfield:DataProvider=new DataProvider(); bloomfield = create.selectData("bloomfield"); list.dataProvider = bloomfield; } } } |
As you can see, each button calls a function to load the List instance with a different set of data loaded into the DataProvider object. It’s just like the first version of the pattern except that a parameter has been added to select from among three data sets. Figure 4 shows the different outcomes in the List component:

Figure 4: Selected List object data
Both in our book and on this blog, you’ve seen other examples of the Factory Method design pattern; so this may not be new as far as a design pattern is concerned. However, it does show a different use of the pattern and how flexible it is. In the next installment, we’ll see how this pattern that provides UI data can be integrated with a State design to develop a video player that is set up so that adding more controls is quite simple.

The Friends with Benefits: Refactoring with Multiple Design Patterns—Part I by William B. Sanders, unless otherwise expressly stated, is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.
Related posts:

Bill Sanders
Thanks. Every single exercise and example helps to get pattern in our brains. Good job!
Hi Mark,
I am very glad to hear that. When I get to the State pattern, I decided to create it as a simple progressive download player because not that many have Flash Media Server.
Kindest regards,
Bill
Hi there!
The example is pretty good as usual! I really like to see Patterns in Action. The point of the post was quite clear.
Now, looking trought a different perspective, I think this is the classical example of an over-design situation, IMHO.
A XML parser/logic/algorithm would be quicker, easier and could easily be encapsulated.
Don´t miss understand me. I´m just raising the importance of not to over-design a simple application.
Cheers
Hi Bill,
I did note:
So, you can use XML files with this design. As with most examples in this blog, we use pretty simple examples with the focus on the architecture and not the size of the task at hand. In that sense, all of the examples are ‘over-designed.’ No disagreement there. The important feature of the designs is whether they can be easily changed without causing a chain reaction the cripples the program.
Given that, let’s put this design to the acid test. Add a concrete Product class (DataSupply is the Product class in this example; so it would be an extension of DataSupply) that pulls data out of an XML file but still places the data into the DataProvider object that can be used with the program.
Thanks for your insights and let’s see that new DataSupply subclass that incorporates your idea!
Kindest regards,
Bill
Why is there no constructor method in each creator class (abstract or concrete) in the Factory pattern? Forgive me if this is a noob-ish question. Thanks.
Hi Jon,
Actually, a constructor method is automatically included if none is specified in ActionScript 3.0. However, a constructor method is only needed to be specified if you are going to have the instantiating object do something other than serve as a platform for using the methods and properties of the instantiated class.
I’ve been having better luck asking myself, Do you really need a constructor function? rather than Why did I leave (an explicit) constructor function out?
Kindest regards,
Bill