17 July 2006
Intermediate
Graphs, charts, video, sound, data entry widgets, animation—these are the elements that make up engaging, powerful online applications ideally suited for education, entertainment, e-commerce, and online retail. Some examples of inventive, retail-based rich Internet applications (RIAs) include Airtreks TripPlanner, Concept Retail, Timberland Boot Configurator, and RbkCustom.
RIA development, whether today in Flex or historically in Flash, has many similarities with application development in languages such as Java, .NET, and C++. Over the years since their introduction, software engineers using these languages have developed best practices for managing and executing the development of large and complex applications. Many of these lessons can also be effectively applied to the development of RIAs.
In this article, I will use the RbkCustom shoe configuration website that Fluid developed for Reebok International to illustrate our particular organization's best practice development tactics.
The RbkCustom Online Shoe Configurator (see Figure 1) allows consumers to configure and purchase selected Reebok shoes by choosing colors and options such as lace locks, outsole styles, and heel clips. Reebok administrators use the accompanying RbkCustom Admin Tools web application to configure the Configurator securely by defining colors, assigning prices, and building "starting point" recipes.
While this article focuses on the implementation of a specific online retail application, several critical planning steps precede the writing of code for any purpose. You first need to understand the tasks that the application must perform, the technologies it will use, and its relationship to other computer systems. Among the most important best practices prior to coding is the creation of a set of artifacts, described next, that outline our goals and articulate how we will meet them.
The functional specification exhaustively describes what the application will do in plain, non-technical language:
The technical specification describes the technical architecture of the entire application in detail, including information about client code, server code, hardware platform, and external system integration. The client portion of this document covers the following topics:
We use industry-standard Object-Oriented Analysis and Design (OOA&D) methodologies to articulate the details of the applications we build. These artifacts are a critical element of the technical specification document described in the prior subsection.
The goal of OOA&D is to understand how the system will accomplish what it needs to do. This documentation helps developers understand their individual tasks and helps coordinate the efforts of multiple cooperating developers.
Unified Modeling Language (UML) is a popular syntax for documenting the classes that make up a software system and the interactions between those classes. UML defines many different types of diagrams. We typically use three of them: class diagrams, sequence diagrams, and collaboration diagrams.
Design patterns help developers learn from the experiences of other developers. They capture recurrent challenges faced by developers and suggest solutions to them, thereby offering elegant and proven solutions to many of the tasks they face. Design patterns constitute a common vocabulary among developers. Their use saves time in the initial development of a system and also facilitates ongoing maintenance.
You must understand design patterns in order to use them effectively. There are many books on this ever-expanding topic. My personal favorite is the timeless classic Design Patterns: Elements of Reusable Object-Oriented Software by Gamma, Helm, Johnson, and Vlissides—a.k.a. the Gang of Four (Addison-Wesley, 1995).
Some design patterns are particularly well suited for use in Flash applications. Some, in fact, are virtually impossible to avoid. For example, the prevalent event notification paradigm in Flash is an articulation of the Observer design pattern. We used the Singleton, Abstract Factory, Bridge, and Template Method patterns (among others) in the RbkCustom application.
As an application grows, the value of good code increases. Well-written ActionScript code is easier to continue developing, especially as new developers are added to the team. It is well worth the nominal extra effort to write good code. Following are some guidelines for writing superior code.
Note: Also be sure to read ActionScript 2.0 Best Practices, which will help you comment, optimize, and format your code consistently across teams to simplify debugging and maintenance.
All variables, properties, parameters, and return values should be strongly typed. For example, rather than vaguely declaring a variable like this:
var firstName;
the type should be explicitly assigned:
var firstName : String;
The compiler can find more errors in code if all its elements are strongly typed, which leads to fewer disruptive runtime problems for end users. Strong typing also makes the code more readable for developers and allows code editors to provide hints and highlighting.
Interfaces in ActionScript define a set of functions that an implementing class must define. If your code interacts with an interface, rather than another specific concrete class that implements the interface, then the application gains flexibility. You can introduce alternate implementations of the interface later without requiring other changes to the application.
We recommend defining interfaces that isolate the aspects of the system that are likely to evolve. Using this approach minimizes the future impact of making these changes. The loading of visual and textual assets into RbkCustom is defined by a custom ILoadTask interface, which provides a simple and consistent mechanism by which we load XML and SWF files into RbkCustom.
All properties and functions should have an explicit scope that is private whenever possible. Maintaining private code is much easier than maintaining public code: the developer only needs to be concerned with the class itself and its subclasses.
ActionScript makes it easy to add properties and functions dynamically to an object using code like this["fu"] = "kung". However, the developer pays a high price for these shortcuts later. Because the compiler will not be able to check the validity of statements like these, typos will generate obscure runtime problems. Code editors will not be able to provide code hints on these illicitly added members either.
Defining functions on the fly causes an arcane memory leak in Flash Player because the activation object sent to the function can never be recovered by the garbage collector. So the bottom line is to avoid dynamic classes and explicitly define all the properties and functions used in your classes.
Classes, functions, properties, parameters, and variables should have descriptive names. It's worth the extra typing to bring additional clarity to more meaningful names. RbkCustom has names like SingleConfigurableProductDataParser, prerequisiteLoadGroup, and getHotPickRecipeByProductId.
Name the elements of your ActionScript code according to the accepted Java language standards. Class names start with a capital letter; other names start with a lowercase letter. For names with multiple words, all words after the first should be capitalized.
Define your properties in Flash privately and provide public getter and setter functions for them. For example, RbkCustom's User class has a private "password" property and public getPassword and setPassword functions to retrieve and assign the password. This naming matches the JavaBeans standard in Java.
We favor this encapsulated approach because it hides a class' implementation details. We can safely and easily change the way we store the password in the User class as long as we don't change the signatures of the getPassword and setPassword functions.
Consistently formatted code is easier to read and maintain. Developers should agree on the usage and positioning of tabs, spaces, parentheses, and brackets. They should also agree to adhere to those conventions consistently.
We favor copious white space to facilitate scanning of the code. We place all curly braces ("{}"), looping, and conditional keywords on their own lines. We also buffer and separate the contents of parentheses by spaces. Colons are preceded and followed by spaces. For example, here's how an IF/THEN/ELSE conditional block looks:
if ( condition )
{
// Code goes here...
}
else
{
// More code goes here...
}
And a function declaration looks like this:
private function doSomething( p1 : String, p2 : Number ) : Boolean
All ActionScript should be generously documented both with JavaDoc-style headers as well as free-form inline comments. Documentation inserted into the source code is most likely to be seen—and appreciated—by subsequent developers.
JavaDoc is the structured documentation style made popular in Java source code. The documentation exists in blocks that start with /** and end with */. Tags such as "@author Andrew Guldman" or "@version 1.0" can be used within JavaDoc blocks to add structure to the documentation.
Every class and function should have a JavaDoc header, as should all but the most transparently obvious properties. Here is an example of a JavaDoc function header from RbkCustom:
/**
* Listener function called when the prerequisite data finishes loading.
*
* @param ev The LoadGroupCompleteEvent broadcast to notify listeners
* that the loading is complete.
*/
private function handlePrerequisiteComplete( ev : LoadGroupCompleteEvent ) : Void
Inline comments are free-form explanatory comments that explain the source code they accompany. Any tricky piece of code should include inline comments. I often write comments before I write the accompanying code to help me plan the details.
Developers typically experiment as they write code. Unused sections are commented out and obsolete comments abound. This situation is to be expected during development. When your code is working properly, however, you should delete these fragments. Update the comments to match the existing code and amputate the rejected code experiments.
Smart developers minimize the amount of code they need to write by reusing existing code—whether written by you, your organization, or somebody else altogether.
Many useful ActionScript libraries and components are available on the Internet. RbkCustom uses third-party libraries to assist with cryptography, drawing, data formatting, and XML parsing. Readily available libraries and components can also assist with a multitude of other tasks, including data loading, data compression, debugging, server communication, testing, and much more.
Carefully read the license for any third-party software before you use it to be sure the license is compatible with your intended usage. Even free open-source software sometimes carries distribution restrictions, especially for commercial usage.
If you can legally use the code you have found, you should critically evaluate its fitness. You can gain valuable insight into the software by browsing online forums, especially support forums. In our own case, we scan the source code to assure ourselves of its quality.
Although the Internet is loaded with reusable ActionScript code and components, finding the right nugget at the right time can be a challenge. If a simple Google search does not succeed, you can try my two favorite backup resources. The Flashcoders mailing list generates a mountain of ActionScript e-mail. Be sure to set up an automated filter for all that incoming Flashcoders e-mail, and learn how to search the contents of those e-mails efficiently.
There are two aspects to effectively reusing code you have written. The code must be configurable enough to be used in a variety of contexts. Hard-coded values should be replaced by parameterized settings. Thorough testing and documentation are mandatory.
The developer of new code also needs to know that existing code is available for reuse. There needs to be some type of repository of code. The larger the organization, the more formal and better organized this repository needs to be. In addition to finding available code, the interested developer needs to be able to learn how to use the code from the documentation.
Fluid has written a command pattern implementation that we have reused within various applications. This implementation is included with this article as a downloadable code sample. The core reusable code supports asynchronous and composite commands as well as simple synchronous commands. This set of classes and interfaces allows us easily to add command implementations to various applications in a simple, consistent, and extensible way.
To implement a synchronous command in an application, for example, we create a class that implements the ICommand interface by defining an execute() function that performs the specific task.
To implement an asynchronous command, we create a class that extends the AbstractAsynchronousCommand class and implements the IConcreteCommand interface. The IConceteCommand interface defines the doExecute() function that the execute() function implementation calls in the AbstractAsynchronousCommand class. Our new class implements doExecute() to perform the necessary asynchronous task, at the conclusion of which it calls the notifyComplete() function.
You can assemble both synchronous and asynchronous commands together into groups of composite commands that run as a unit in sequential order. This ability to aggregate groups of commands allows us to flexibly reuse commands themselves, as well as the command pattern framework.
Multideveloper projects present unique challenges. Because the Flash source FLA file format is binary, it is difficult for multiple developers to work on the same source file. The management of large Flash projects is further complicated because both designers and engineers will have reason to modify FLA files during development.
For these reasons, large Flash projects require more planning, coordination, and discipline than large projects built with text-based source files, such as HTML, JavaScript, or Java. We adhere to a few rules of thumb to help us effectively manage these projects.
With few exceptions, engineers who write ActionScript do not make changes to FLA files. Designers create the visual elements in FLA files, which the code written by engineers then manipulates. Two techniques help us obey this rule:
Object.registerClass function to make that association. The benefit of this approach is that it moves the creation and maintenance of the association out of the FLA file and into ActionScript files.We create multiple FLA files to implement the user interfaces of large projects. There is typically one "shell" FLA that contains the main user interface and any commonly used pop-up windows, assets, and dialog boxes. The other modules of the application are defined in separate FLA files. These FLA files are compiled into SWFs, which are dynamically loaded into the shell SWF at runtime using some permutation of the loadMovie function.
An extension of this approach is the generation of multiple SWFs from a single FLA. This strategy is particularly useful if multiple, similar visual elements are used in distinct circumstances. We define separate MovieClip symbols in the library for each visual element, which can share visual subcomponents. We then generate SWFs for each of them by right-clicking the symbol in the library and choosing Export Flash Movie from the pop-up menu. One complication of this technique is keeping track of all the SWFs you export. To address this issue, we name the SWFs the same as the MovieClips that spawn them.
Maintaining a consistent look and feel across the application is one challenge in aggregating many separate SWFs into a single coherent application. We improve the visual consistency of the user interface by using the same global skinning mechanism across all the separate FLAs. When we use the Adobe Flash MX2 component set, we use style sheets to accomplish this.
When you are finished developing an RIA, you need to deploy it so users of the application can access it. Flash applications can run in a variety of contexts, such as desktop applications or touchscreen kiosks. However, the most common deployment mechanism for Flash applications—and the one I focus on here—is running in a web browser.
When running in a web browser, the Flash application is embedded in an HTML page. Flash and the HTML page can interact with each other using JavaScript. This interaction is most useful for the following purposes.
Flash detection: Before anyone browses to a Flash application, we need to ensure that the user has at least the minimum required version of Flash Player. There are a number of ways to accomplish this, all of which use HTML and JavaScript. The Flash Player Detection Kit is one excellent approach and is included in Flash Professional 8. (For later versions of Flash, we recommend using SWFObject 2.)
Configuration parameters to Flash: You can send static, fixed parameters from HTML to Flash to configure an application. For example, we can configure RbkCustom using a parameter set in HTML to show a warning dialog box every time it encounters an erroneous recipe:
<param name="flashVars" value="showRecipeErrors=false">
In addition, JavaScript can capture query string parameters sent dynamically to an HTML page and forward them to the Flash application as flashVars. RbkCustom supports the sharing of shoe configurations by e-mail using recipe and product query string parameters that the Flash application receives as flashVars.
For example, consider the following URL:
http://www.rbkcustom.com/?productId=3&recipe=2100%2C4963%7C2101%2C6604%7C2102%2C6605%7C2110%2C5000%7C2111%2C4988%7C2112%2C6512%7C2113%2C6513%7C2114%2C5013%7C2115%2C6514%7C2116%2C6515%7C2120%2C5037%7C2121%2C6533%7C2122%2C6534%7C2130%2C5049%7C2131%2C5050%7C2132%2C6093%7C2133%2C5062%7C2134%2C6095%7C2135%2C6634%7C2136%2C6096%7C2140%2C5100%7C2141%2C6097%7C2142%2C5112%7C2143%2C6099%7C2144%2C6638%7C2145%2C6100%7C2150%2C5218%7C2151%2C5150%7C2152%2C5161%7C2153%2C5173%7C2154%2C5184%7C2155%2C6089%7C2156%2C5207%7C2157%2C5219%7C2158%2C6522%7C2159%2C5242%7C2160%2C6075%7C2161%2C6614%7C2170%2C5699%7C2171%2C6508%7C2172%2C5275%7C2173%2C5299%7C2174%2C5310%7C2180%2C6505%7C2190%2C5378%7C2191%2C5333%7C2192%2C5344%7C2193%2C5356%7C2194%2C5367%7C2195%2C6648%7C2196%2C5390%7C2197%2C5402%7C2198%2C6109%7C2199%2C5425%7C2200%2C5448%7C2201%2C6544%7C2202%2C6545%7C2210%2C5472%7C2211%2C5693%7C2212%2C5694%7C2220%2C5484%7C2221%2C5486%7C2222%2C6066%7C2223%2C6067%7C2230%2C5512%7C2231%2C6610%7C2240%2C5536%7C2241%2C6500%7C2242%2C6501%7C2250%2C5560%7C2251%2C5684%7C2252%2C5685%7C2260%2C5572%7C2261%2C6060%7C2262%2C6061%7C2263%2C5596%7C2264%2C
These query string parameters cause RbkCustom to open a lovely orange, white, brown, and yellow Supercourt shoe (see Figure 2).
Accessing JavaScript APIs: Some third-party software packages offer JavaScript integration, but not direct Flash integration. Flash can invoke JavaScript functions to utilize these JavaScript APIs. For example, RbkCustom uses the HitBox analytics package from WebSideStory (now Omniture) this way. Similar JavaScipt integration is available to a variety of competing analytics packages including Google Analytics and Fireclick. Other JavaScript APIs provide access to mapping, e-commerce, weather, and more.
Eolas patent: The Microsoft Internet Explorer browser was changed in early 2006 to require end users to explicitly click ActiveX controls in order to activate them. This effectively put a speed bump in front of Flash applications. Fortunately, there is an easy workaround: the <object> and <embed> tags used to position the Flash application within the HTML file must be rendered by JavaScript that is contained in a separate external file.
For more information about this situation, and its solution, visit the Active Content Developer Center.
To increase the chances of deploying the software smoothly, your development team must test it several ways. The earlier you identify bugs, the more easily you can fix them. For this reason, we emphasize rigorous unit testing by our individual developers to ensure that each individual piece of code behaves as it should.
When functional subsystems of the application are complete, somebody unfamiliar with the application proceeds with testing. Developers know the system too well and often overlook basic problems. We use the open-source Bugzilla bug-tracking system to ensure that the bugs we find are fixed in a timely manner.
To test the performance of some critical systems, we establish minimum guidelines in the technical specification and then conduct performance testing to ensure that we have met those requirements.
We usually have two server environments for our projects, one for production deployment and one for development and testing. We make these environments as similar as possible: same number of servers, same hardware, same operating systems, and same software. The similarity of our testing platform gives us increased confidence that our production deployments will be smooth.
RbkCustom has two servers in each environment: a database server and a second, physical server computer that runs both the web server and application server software. Our production environment is hosted by VeriCenter and runs on leased servers. We operate the development environment ourselves. After investing a couple of days in the initial setup, our resident IT wizard spends a couple of hours each month maintaining the development environment.
We manage all of our code using a version control system that helps us coordinate the work of multiple developers throughout the development process. Version control also helps us deploy our projects into production in a controlled manner.
Prior to deploying the project, we create a tag in the version control system that identifies the specific versions of all the files that are involved in the production deployment. This tag includes all the source code (ActionScript code and FLAs) required to regenerate the deployed SWFs. We only deploy tagged SWFs into production.
For RbkCustom, we have one set of tags for the consumer-facing Configurator application, a second set for the Administrative Tools application, and a third set for the Java application server code that supports them. The Configurator tags have the structure RBKCUSTOM_CONFIGURATOR_1_2_3. The first digit is the major version number, the second is the minor version number, and the third is the subversion number. Similarly the Administrative Tools tags are named in the pattern RBKCUSTOM_ADMINTOOLS_1_2_3, and the server code is tagged with names like RBKCUSTOM_SERVER_1_2_3.
This tagging serves two purposes. If a subsequent deployment causes problems, we can easily revert to the prior version using the prior tag. Sometimes we need to fix a deployed version after subsequent development has occurred that cannot be put into production. In this circumstance, we copy the source code that was used to generate the problematic production build, make changes to that code, and then compile new SWFs for immediate use in production.
A few rules of thumb can help you develop rich Internet applications effectively. Prepare yourself by thoroughly understanding the applications you are building. Creating specification documents and diagrams helps you systematically research your applications and share your knowledge with prospective end users and other team members.
Writing consistently formatted, well-documented, and well-structured code makes it easier for you to work with the code, and improves the support provided by compilers and editors. We minimize the amount of code we write by reusing existing code. This reuse improves both the efficiency of development and the quality of the code.
Flash presents unique development challenges because the binary FLA file format is difficult to share among developers. Two handy rules assist us in working with FLA files: engineers never edit FLAs, and we have at least one FLA per concurrent designer working on the project.
Before deploying your RIAs for public consumption, make sure they are ready for prime time. Write HTML and JavaScript containers so that they cooperate with Flash. Thoroughly test your applications to ensure that individual components behave properly, and that entire applications run as specified. Finally, use a version control system that gives you a security blanket that allows you to reactivate—and even modify—prior versions of the application in cases of emergencies.
Visit other innovative RIAs here:
For more information about the topics covered in this article, check out the following resources:
| 04/23/2012 | Auto-Save and Auto-Recovery |
|---|---|
| 04/23/2012 | Open hyperlinks in new window/tab/pop-up ? |
| 04/21/2012 | PNG transparencies glitched |
| 04/01/2010 | Workaround for JSFL shape selection bug? |
| 02/13/2012 | Randomize an array |
|---|---|
| 02/11/2012 | How to create a Facebook fan page with Flash |
| 02/08/2012 | Digital Clock |
| 01/18/2012 | Recording webcam video & audio in a flv file on local drive |