Accessibility
Thomas Ortega II

Thomas Ortega II

Silicon Valley Flex User Group
360|Flex conference
Blog

Created:
13 August 2007
User Level:
Intermediate
Products:
Flex

Graduating from hack to architected development – Part 2: Adding architecture to your code design

Note: This article was created based on Flex 2. Minor changes in the description and code may be necessary before it can be applied to Flex 3.

Once developers become experienced with Flex applications, they tend to regret some of their coding choices to solve a particular problem rather than planning the structure of the application. The spaghetti code and hacks that got the job done earlier are now becoming roadblocks to scalability, code reuse, and project maintainability.

In this three-part series, I describe the development of a sample contact manager application.

  • In Part 1: Identifying code hacks, I showed a working application completed with little or no planning. In this part, I take the exact same application and remove the hacks, replacing them with a more advanced approach to programming the application. This installment also introduces techniques for coding an architected application, using concepts such as custom events and value objects.
  • In Part 2, I take the exact same application and remove the hacks, replacing them with a more advanced approach to programming the application. Part 2 introduces concepts such as custom events and value objects.
  • In Part 3: Revising code within the Cairngorm framework, I take the application development to the next level, implementing the application within the Cairngorm framework. I also show you how to organize your classes (events and value objects) and use advanced methodologies (controllers and locators)—all of which will make your code easier to read, maintain, and extend.

Requirements

Flex Builder 2 (SDK included)

Sample files:

Note: Extract these files into a new Flex project (if you are using Flex Builder) or a generic folder on your system (if you are using the Flex SDK and another editor).

Prerequisite knowledge

To benefit most from this series, it is best if you:

  • Read Part 1: Identifying code hacks of this series to understand what has been identified as coding hacks and spaghetti code.
  • Have already programmed a few Flex applications.
  • Are able to distinguish between MXML and ActionScript 3.0 code at a glance.

Value objects and custom events

In Part 1: Identifying code hacks there were three files for the sample application:

  • main.mxml: main application file which houses the two component files
  • contactListComponent.mxml: DataGrid list of contacts
  • contactDetailsComponent.mxml: contact form

These files house the markup language that defines the layout of the application components as well as transitions. These items don't change much from the previous version. What is dramatically different inside these very same files is the application logic that controls what happens to the components that are laid out. I'll discuss the details of how the old hacks are replaced with best-practice code throughout this article.

The most prominent change to the project is the addition of two new directories, "vo" and "events". Just the fact that there are directories at all is a testament of better code organization. A big mistake that new developers tend to make is putting all the code into the project's root directory. While this is simple and seems harmless when you write simple apps with five or so files, it causes headaches when the file count gets to 50+, and becomes impossible to manage when the file count reaches hundreds or thousands.

Value objects

Value Object, abbreviated as VO, is a design-pattern principle. According to this principle, related items describing the same entity should be grouped and passed together as a single object. The originally independent yet related items then become properties in this new VO class. Whenever you need another copy of the VO, you simply create a new instance of that class. A common practice is to put all of your VO classes into a directory called vo. When you need to modify a particular VO, it's much simpler to do so when they are all in the same directory.

In this version of the app, we have added only one VO: ContactInfo. As the name implies, this is used to describe and transport information about a contact. If you open up the ContactInfo.as file, you'll notice one obvious thing:

public class ContactInfo

The class name and the filename, in this case ContactInfo, must match. If they do not match, you will get an error. This rule goes not only for VOs, but for any custom class you write for your applications.

Custom events

The second new directory you find is "events". In this directory, you put in all the custom events that your application is going to use. Events are the driving force behind any Flex application, i.e. events make things happen. When you push your mouse button down, for instance, a mouse down event is fired (along with many others). This event causes the button to change its state/appearance. Also, any code you put in that button's mouseDown event handler also runs. While the Flex/Flash frameworks have hundreds of built-in events for you to use, you quickly begin to see how a custom event can greatly assist your app.

The two custom events built for this version of the application are ShowContactEvent and UpdateContactEvent. ShowContactEvent is fired when we need to show a contact's details and UpdateContactEvent is used to update a specific contact's details with updated information. Aside from the class names matching the filenames, we notice something different about the custom event class declarations:

public class ShowContactEvent extends Event
public class UpdateContactEvent extends Event

They use a new keyword: extends. Extending base classes to create your own classes is very common in Flex because you can use classes as building blocks and add more functionality to them when you need it. You can do the same for any class, i.e. you can create your own custom button by extending the base button class. What this means is you get all the built-in properties and methods of that base class in your new custom class for free! In the button example, you get all the mouse events, button states/styles, and properties (such as visible and enabled) without having to recode that for your custom button.

Dropping hacks for best practice standards

In Part 1, I showed how hacks were used to accomplish the application logic. In this article, I'll show you how the code accomplishes the same tasks using best practice standards. I suggest you refer to each section in Part 1 and then reread it here in Part 2 to see how I've updated it based on the better coding practices.

Understanding and using a custom ActionScript class

I set up the contactListAC:ArrayCollection in this version exactly as I did in Part 1. This is the variable that is actually going to house the contact list. The only thing that changes is how I hard-code the first entry. In the previous version, I added a generic object to the ArrayCollection:

contactListAC.addItem({First:"Tom",
Middle:"",
Last:"Ortega",
Cell:"555-222-3333",
Home:"555-333-4444",
Address:"1313 Mockingbird Lane",
City:"San Jose",
State:"CA",
Zip:"95134"});

In this version, we add an instance of our new ContactInfo value object instead of an inline object:

contactListAC.addItem(new ContactInfo("Tom",
"",
"Ortega",
"555-222-3333",
"555-333-4444",
"1313 Mockingbird Lane",
"San Jose",
"CA",
"95134"));

I'll explain what's going on in that line a bit later. Right now, let me dissect the ContactInfo.as class file to explain its various parts. Though we call this custom class a value object, that is just a naming convention. There's nothing specific to this custom class that makes it a value object per se. It could just as easily be called a contact container or an info object. What makes it a value object is how we use it, not how we code the class file itself. The purpose of using terminology like value object is that it is a universally utilized design pattern. Therefore, new developers who are familiar with that term will be able to understand the code immediately.

Since I'm standardizing the code by adding a value object class, there's another thing I can fix up. Looking at the original addItem method, I can see that my properties (First, Middle, etc.) begin with capital letters. While there's nothing technically wrong with using capital letters to begin properties/variables names, there is something wrong with it convention wise. Capitalizing the first letter in a name commonly denotes a class name. Using all caps (as you'll see later) is reserved for constants. Therefore, when I create the value object, I go ahead and drop the non-conventional use of caps in the properties. I also update the dataField property of my DataGridColumns in contactListComponent.mxml and various variables in contactDetailsComp.mxml to use the non-capitalized versions of the property names (i.e. swap out "Address" for "address", etc.).

As I go through the ContactInfo class, remember these explanations pertain to any custom class. When you choose New > ActionScript Class inside of Flex Builder, you'll be utilizing the following skillset in every ActionScript class you build:

package vo

A package is an assortment of items put together in a single, convenient location. In the real world, that convenient location is usually a box or gift basket. In the coding world, the convenient location is a directory in your project. Therefore, looking at that line, we know that we can find this class file in the vo directory of the project. You use dot notation to indicated nested directories:

package vo.contacts

Therefore, if "contacts" were a directory inside of the "vo" directory, then any classes in the "contacts" directory would have the package declaration shown above:

[Bindable]
public class ContactInfo

The [Bindable] is used in front of any variable you would like to make bindable. Only in this case, I want to make all properties of this class bindable. Instead of having to put [Bindable] in front of each property, I just use it once in front of the class declaration and Flex knows how to make all properties bindable.

Next, you see some of those property declarations:

public var first:String = new String();
public var middle:String = new String();
...
...
public var zip:String = new String();

These properties are what hold the various bits of contact information. Notice that I have dropped the first-letter caps that I originally had in the hacked code. The last bit of the code that needs explanation is the constructor. A constructor is a very special function in a class file. It is executed before any other function when you instantiate a new class instance. It lets you add some code to be executed every time the class is instantiated. Let's take a look at ContactInfo's constructor:

public function ContactInfo(
    first:String="",middle:String="",last:String="",cell:String="",home:String="",street:String="",city:String="",state:String="",zip:String="")
{
    this.first
    = first;
    this.middle
    = middle;
...
...
    this.zip
    = zip;
}

There are some things you notice about the constructor:

  • It is always nested inside the class declaration; likewise, the class declaration is always nested inside the package declaration.
  • The name of the constructor function must match the name of the class; likewise, the name of the class must match the name of the file.
  • The constructor function also does not return anything, i.e. there's no :void added after the closing parenthesis.
  • There are parameters in our constructor: zip:String="". This is a fancy way of saying: If the user wants, they can pass in a parameter called zip, which will set that value inside the class instance. If they don't, then the default value of the zip parameter will be set to " ". This lets you pass in some initial values to use in your class when it is constructed. Parameters are not required in a constructor; just add them when you need them.

Inside the constructor function, you see this bit of code:

this.zip = zip;

The keyword this refers to the class itself. Therefore, you could read that assignment as: "Make the class property named zip equal to the value of the function parameter named zip." The reason I put this into the constructor was so that we could easily send in the contact info. Let's go back to the hard-coded entry in main.mxml.

To help you understand better, I'm going to add some line breaks:

contactListAC.addItem(
new ContactInfo("Tom",
"",
"Ortega",
"555-222-3333",
"555-333-4444",
"1313 Mockingbird Lane",
"San Jose",
"CA",
"95134")
);

The green is just the common addItem method you use on an ArrayCollection. Nothing new there. The orange code is me initializing a new ContactInfo class.  I now know that the information I pass in, i.e. "Tom" is going to autopopulate the class property called first.

Introduction to casting and custom events

The way to view a contact's details has stayed the same. The user merely needs to click an entry in the DataGrid in contactListComponent.mxml:

<mx:DataGrid id="mainList" dataProvider="{contactListAC}" itemClick="itemClicked(event)"
    width="100%" height="100%">

You see that I have set up an itemClick event handler. Whenever a user clicks the DataGrid, I call the itemClicked function. In Part 1: Identifying code hacks we saw the following hack used in the itemClicked function to tell the parent file (main.mxml) that an item was clicked:

this.parentDocument.contactSelectedFunc(
    mainList.selectedItem,true);

This time you see the following code in the itemClicked function:

selectedItem = mainList.selectedItem as ContactInfo;

I set the selectedItem variable equal to the selected item in the list. However, there is the extra bit of code that says as ContactInfo;. That bit of code is saying, "I know that I'm inserting ContactInfo instances into the contactListAC that is the grid's dataProvider. Therefore, when I get the selectedItem of the mainList, I know that item is going to be of type ContactInfo." Similarly, in the variable declarations at the top of contactListComponent.mxml, you see the following:

[Bindable] public var selectedItem:ContactInfo;

To recap, I know I'm inserting ContactInfo objects into the contact list. I then know that I want to set the value of selectedItem, which is of type ContactInfo, to the currently selected item in the list. While I know this will all work at runtime, Flex Builder knows only that selectedItem is going to be a ContactInfo object at compile-time. As far as the items inside of contactListAC, Flex Builder figures you can add any type of object you want in there and it wants some reassurance that the objects ixn there are indeed ContactInfo objects. Using the as keyword is known as casting. You are taking a variable of a known type (mainList.selectedItem, which in this case Flex Builder defaults to the generic type object) and are casting it into a different type (in this case, ContactInfo). Casting gives Flex Builder that reassurance it wants that you know what you're doing and lets you compile the app so you can prove it at runtime.

The next line of code in the item function introduces the concept of custom events:

dispatchEvent(
    new ShowContactEvent( 
        ShowContactEvent.SHOW_CONTACT_TYPE, 
        selectedItem, 
        true)
    );

ShowContactEvent is a custom ActionScript class, just like ContactInfo. Open up the ShowContactEvent.as file to see what it looks like. The first line is the package declaration.as:

package events

This line informs you that the ShowContactEvent.as is located in the "events" directory of your project. Next, inside the package declaration you see some import statements:

import flash.events.Event;
import vo.ContactInfo;

This tells you that you will be using the Event class and ContactInfo class inside of the custom class. Next comes the class declaration:

public class ShowContactEvent extends Event

You see a new keyword here: extends. All that says is that this class is based off another class. Remember earlier in the article, I said you can create a custom class, base it off another class, and inherit all its properties and methods? This is how you do it. Therefore, you know that ShowContactEvent will have all the same properties and methods as the base Event class, in addition to whatever new properties I add.

Next up are the new properties I'm adding. I'll only call out one special property:

public static const SHOW_CONTACT_TYPE:String = "ContactSelectedEvent";

This has a few unique qualities to it:

  • The keyword static means shared. Whereas normally a class instance gets its own copies of the properties to fill with different information as they see fit, a static property is shared across all instances of the class. Imagine if there was a custom class named sibling. Each sibling would have a unique value for the name property but they would all share a static property called parents.
  • The keyword const means constant, never changing, as opposed to the keyword var which means variable, prone to change. You use this keyword when you want to set a value and never have it change. To return to the sibling class example, you'd want to set a property called species to the const value "Human".
  • The property name itself is in all uppercase with underscores separating the words. This is a best practice used to visibly denote a const value in your code. This way no matter where that value is used, one look will inform you and other programmers that the value of that property is a constant.

The next item we will review is the constructor:

public function ShowContactEvent(type:String, selectedItem:ContactInfo,
    showContactDetails:Boolean, bubbles:Boolean=true, cancelable:Boolean=false)

What's unique about this constructor is that there are both required and optional parameters. You can differentiate them by looking to see if they have default values or not. In this case, we see that type, selectedItem and showContactDetails are required, while bubbles and cancelable are optional. The rule to remember when mixing required and optional parameters in a function call is that the required parameters must always come first.

The last item of code I'd like to point out is the super call inside of the constructor body:

super(type,
    bubbles, cancelable);

The super call is just a fancy way of saying, "Go ahead and call this exact same function in the class that I'm extending." You can use super for any method that your custom class and base class share. Typically, you do this by overriding the method in your custom class; however, we have no examples of that in our code. Using super inside a constructor is a bit different because rather than calling the function with the same name in the base class (which won't exist), it just calls that base class's constructor instead.

Now, we can go back to the code inside our itemClicked function inside of contactListComponent.mxml:

dispatchEvent(
          new ShowContactEvent( 
                          ShowContactEvent.SHOW_CONTACT_TYPE, 
                          selectedItem, 
                          true)
          );

We now see that this line fires off a ShowContactEvent when an item is clicked. We put the selected item inside the event as well as set the showContactDetails boolean flag to true. Because we set the default value of the bubbles property to true, this event will flow upward through the parent chain until it reaches the end. To find out more about events, read the Handling events Quick Start in the Flex Developer Center.

In the file main.mxml, see the following line of code in the initFunc function:

addEventListener(
    ShowContactEvent.SHOW_CONTACT_TYPE,contactSelectedFunc);

What this does is listen for the ShowContactEvent.SHOW_CONTACT_TYPE to come through. When it does, it then calls the contactSelectedFunc function and passes in the ShowContactEvent object. The beauty of using this versus the this.parentDocument.contactSelectedFunc hack in Part 1 is that you can now move contactListComponent.mxml as far deep as you want in the program and it will always work. With the hack, it only works if contactListComponent.mxml is a child of main.mxml.

Now if we take a look at contactSelectedFunc in main.mxml, you'll see the code is pretty much the same only now we use the event instead of individual parameters, plus we see that contactDetailsComp.selectedItem is also now using the ContactInfo class:

public function contactSelectedFunc(event:ShowContactEvent):void
{
    if (event.showContactDetails)
    {
          currentState="ShowContact";
          if (event.selectedItem == null)
          {
          contactDetailsComp.selectedItem
    = new ContactInfo();
          contactDetailsComp.currentState
    = "AddContact";
          }
          else
          {
          contactDetailsComp.selectedItem
    = event.selectedItem;
          contactDetailsComp.currentState
    = "";
          }
    }   
    else
    {
          currentState="";
    }
}

Editing a contact's details

Review Part 1 to see how editing works. The only line that is changed in contactDetailsComponent is in the editSubmit function that's called when the button is clicked:

this.parentDocument.updateContactListFunc(selectedItem,addNew);

In this version, I fire another custom event:

dispatchEvent(
    new UpdateContactEvent(
    UpdateContactEvent.UPDATE_CONTACT_TYPE,selectedItem,addNew)
);

Take a look at the UpdateContactEvent.as file. It is in the same package (directory) as ShowContactEvent.as and is very similar in content. Therefore, I won't go over the UpdateContactEvent class file in depth.

In the file main.mxml, we see the following line of code in the initFunc function:

addEventListener(
    UpdateContactEvent.UPDATE_CONTACT_TYPE,updateContactListFunc);

Like the previous event listener, this lets you know that when the UpdateContactEvent.UPDATE_CONTACT_TYPE comes through main.mxml, it calls the updateContactListFunc function. Besides now utilizing the event instead of numerous parameters, the biggest change is this:

contactListComp.dispatchEvent(
    new UpdateContactEvent(
                    UpdateContactEvent.UPDATE_CONTACT_TYPE, 
                    event.item,
                    event.newItem,
                    false)
);

What this does is take the event and redispatches it inside of the contactListComponent.mxml file. In there, we see the following eventListener in the initFunc function:

addEventListener(
    UpdateContactEvent.UPDATE_CONTACT_TYPE,updateTable
    );

Looking at the updateTable function in the contactListComponent.mxml file, notice that the function is rewritten to utilize the event:

public function updateTable(event:UpdateContactEvent):void
{
    mainList.selectedItem =
    event.item;
}

Adding a new contact's details

The logic here is pretty much the same as it was in Part 1. The major difference is what happens when the "Add New" button is clicked. The button's click event calls the addNewClick() function. In Part 1, this function had this horrible hack:

public function addNewClick():void
{
    this.parentDocument.contactSelectedFunc(null,true);
}

Now, we use a much more robust custom event solution:

public function addNewClick():void
{
    dispatchEvent(
          new ShowContactEvent(
               ShowContactEvent.SHOW_CONTACT_TYPE,null,true)
    );
}

Using your new found logic about events, you should review main.mxml and follow the flow on what happens after this.

Hint: Refer to the edit steps above and the Adding a new contact's details section in Part 1 to help you if you get lost.

Hiding the contact details form

Once again the only change is that I drop the hack and use a more robust custom event solution. The following is the code lines used in Part 1:

public function hideDetailsClick():void
{
    this.parentDocument.contactSelectedFunc(null,false);
}

Here are my new code lines:

public function hideDetailsClick():void
{
    dispatchEvent( 
          new ShowContactEvent(
          ShowContactEvent.SHOW_CONTACT_TYPE,selectedItem,false)
    );
}

Where to go from here

As you can see, moving from hacks to a more architected development mentality is not a simple step. It is very easy to find yourself using the this.parentDocument hack. It will take some time before you get used to architecting your application to utilize custom classes such as value objects and custom events. However, the benefits of a more robust solution far outweigh the initial pains of changing your mentality.

While this part of the series is quite long, it is necessary to have you understand the new concepts that were introduced. It may take a couple of reads and walkthroughs of the series to fully grasp all the concepts. Take the time to learn these concepts. Part 3: Revising code within the Cairngorm framework takes this same application and implement it in the Cairngorm framework. Without a solid understanding of the concepts introduced in this part of the series, you may find yourself even more confused by trying to learn Cairngorm. If you find yourself stuck with some of these concepts, feel free to send me your questions.

About the author

Thomas Ortega II is an active member of the Flex community and works for Workday, Inc. He runs the Silicon Valley Flex User Group where he gives free Flex training. He also is a coordinator of the 360|Flex conference. You can find Tom on his blog.