
Tom Muck is coauthor of six Macromedia-related books including O'Reilly's Flash Remoting: The Definitive Guide, and the bestseller, Dreamweaver UltraDev 4: The Complete Reference. He is an extensibility expert focused on the integration of Macromedia products with ColdFusion, ASP, PHP, and other languages, applications, and technologies and is a founding member of Community MX (http://www.communitymx.com). When not in front of the computer, Tom works on his Hong Kong movie collection. Once an aspiring heavy-metal musician, Tom is now content in his old age to sit on the porch playing the blues on his harmonica and banging out Robert Johnson songs on his beat-up acoustic guitar.
Excerpted from http://www.oreillynet.com/pub/au/1183
This full chapter, excerpted from Tom Muck's "Flash Remoting, the Definitive Guide", published by O'Reilly, deals with ActionScript Best Practices. While the book is geared more specifically towards Remoting, the practices discussed in this chapter are very transferable to all ActionScript development.
Copyright © 2003 O'Reilly Media, Inc. All Rights Reserved. | ![]() |
| This content is excerpted from the above-named O'Reilly publication, with permission, by agreement with ActionScript.org. | |
The issue of best practices is a subjective topic. One person's idea of a best practice may be someone else's idea of a worst nightmare. As in all forms of programming, there is no single right way to perform a given task. For that reason, this chapter presents some of the common tactics employed by Flash Remoting developers. It is up to the developer to measure the options carefully and decide on the best course of action for a particular application.
We've touched on numerous best practices throughout the earlier chapters (see "best practices" in the index). Here we look at the big picture and cover additional best practices you should seriously consider. Refer also to the Macromedia white papers and articles on best practices cited in Appendix B.
A Flash Remoting application can be constructed by one person or a team of designers and programmers. If working in a team environment, Flash Remoting utilizes several different technologies that can be easily broken down into tasks for different types of developers. In your team, individuals might wear different hats at different times, so there will be some overlap, but the division of tasks is fairly clear.
The designer concentrates on the visual and audio design of the application and deals with the look and feel of the application. He creates all the interfaces; he chooses the fonts, colors, graphics, and other elements of the application. He may know ActionScript and be able to program some functionality, or he may not. He should be able to create an application interface from specifications and be flexible enough to change visual elements easily.
The client-side ActionScript programmer is responsible for all of the interaction in the application, and she works with the designer to implement this functionality. She might create the code that calls the various interface elements to display in the movie, and she would also create any code that is related to user input. The ActionScript programmer should know the Flash programming environment inside and out and be able to bring the project specifications to life.
If the designer has not completed a particular section, the interface should work as it stands, using dummy methods where user interactions or remote service calls might be (for example, if a user clicks "Login," an alert message might say "User logging in").
The Flash Remoting programmer is also an ActionScript programmer, but he is responsible for connecting to the remote services and providing hooks for the client-side ActionScript programmer in the form of an API. He should work closely with the server-side developers to build the client-side code that calls remote services accurately. He should be able to test his service calls at all points, using dummy methods on the server that the server-side programmer has set up.
The database programmer is responsible for setting up the database, including table definitions, relationships, and all stored procedure code necessary. She should also work closely with the server-side programmer. Typically, in an application that uses a database, the database needs to be in place before the server-side services are developed. The database is frequently the first part of the application to be built. All parts of the application revolve around the database.
The database programmer is also responsible for exposing items to the server-side programmer. From an ActionScript perspective, this is the equivalent of exposing methods or functions to the team. The database programmer exposes views, stored procedures, and possibly tables (in a MySQL environment) to the server-side programmer.
The server-side programmer might be versed in CFML, SSAS, Java, C#, VB.NET, or PHP. He should be able to create the remote services that can be accessed from the Flash movie. He should also be able to create HTML interfaces (test harnesses) for testing the remote services so that the server-side services can be completed before bringing Flash into the equation.
The server-side programmer exposes methods for the Flash Remoting ActionScript programmer to utilize.
The HTML developer ties the whole application together on the HTML page and provides necessary code for alternate pages (if needed) and Flash detection. She also must create the HTML pages in such a way that they blend seamlessly with the Flash application. The HTML developer should work closely with the designer.
Copyright © 2003 O'Reilly Media, Inc. All Rights Reserved. | ![]() |
| This content is excerpted from the above-named O'Reilly publication, with permission, by agreement with ActionScript.org. | |
A Flash Remoting application is a client/server application.
You should have a clean separation of client and server duties,
however. The server-side services should be operable in any
situation, whether being accessed by a Flash interface, HTML
interface, desktop application, or other web service. For that
reason, it is not advisable to use the Flash
object on the server. In Example 2-5, you saw the
HelloUser service written using C# in an ASP.NET
environment:
I presented it because it is a part of Flash Remoting that you should
know about—and it is frequently an easy way to accomplish a
task—but it is not always a good way to code your remote
service. The Flash object is available and easy to
use, but you should carefully consider the consequences of using the
Flash object, as it ties the remote service to the
Flash application (preventing you from building a non-Flash interface
to the service).
There are several other considerations to separating the client and server. Flash Remoting raises a dilemma—where does the functionality belong? Some functionality is plainly client-side functionality, and some is plainly server-side. Some of it might be in-between and could go either way. For example, you can filter large sets of data in the middle tier, where you have a known environment and resources, versus doing it on the client side within Flash, which, depending on the client's machine, might not perform well. In this case, you may want to initially sort the data set on the server, and then have any user-initiated sorts occur within the Flash Player. This is a good trade-off between client-side processing concerns and the extra bandwidth required to transfer data sets to and from the server.
The database should handle as much data processing as possible, because that is its function and it is good at it. Such things as sorting and filtering recordsets, especially large recordsets, should be left to the database whenever possible.
An example of how you might enhance a server-side method to be more versatile and separate from the client is to manually manipulate your resultset before sending it to Flash. To demonstrate what I mean, think of a resultset that feeds a ComboBox. Frequently, you want to add a display item or an option for "All" in a ComboBox. One of the ways to do this is to add the text manually on the client after the resultset is returned. Sometimes, it makes sense to add the logic to the server so that the client merely has to display what is returned, without doing any manipulation. Performing the processing on the server side makes for a faster client experience and, as an added benefit, if you have to create an HTML interface for the same type of ComboBox, you don't have to do any client-side manipulation of code in your HTML page either. Example 12-1 shows the ColdFusion code to manipulate a Query object on the server, which populates a client-side ComboBox.
In this case, we could have added the row on the Flash client using
ActionScript code, but manipulating UI components can slow down an
application—especially if you have more than a few in an
interface. Example 12-1 shows another aspect of
ColdFusion—the ability to cache a resultset. The attribute
cachedwithin="#CreateTimeSpan(7,
0, 0, 0)#"
creates a seven-day cache; the database is hit only once per week for
this query.
This type of functionality can even be included in your database in a stored procedure:
[code]CREATE PROCEDURE spGetCategoriesDropdownThe stored procedure returns the resultset with the values already in place for populating the drop-down list.
Copyright © 2003 O'Reilly Media, Inc. All Rights Reserved. | ![]() |
| This content is excerpted from the above-named O'Reilly publication, with permission, by agreement with ActionScript.org. | |
The Flash Remoting application has many possible failure points. One of the more frequent failure points is the communication between client and server. Server-proofing involves testing the application in serverless environments to guarantee that the application will fail gracefully if the server is unavailable.
There are many possible reasons for communication failure:
This can happen during peak hours and may be a limitation of the end user's Internet Service Provider (ISP), or it may be tied to a global Internet virus, which seems to occur more and more each year.
If the end user has disconnected from the Internet, how will your application respond?
This can happen when your ISP is rebooting a server, the server is down for maintenance, etc.
Whatever the reason, your application needs a reliable way to recover from the lack of a connection. In an HTML page, this is not a problem; the browser will force a timeout after a specified number of seconds waiting for a response. In a Flash movie, it is your responsibility to provide a fallback mechanism to handle the lack of a connection. You can do this in two ways:
In some applications this is not possible, but you may have an application that works well offline, such as an email program that allows a user to read the contents of previously downloaded email or compose an email offline.
The user should not see system error messages and other cryptic messages. Instead, handle the error gracefully in the ActionScript code and display a comprehensible message to the end user.
The System.onStatus event should be assigned to a
function in your application so that any failed remote calls will be
handled gracefully:
This code displays an HTML error page to the user when a connection to Flash Remoting cannot be made.
Copyright © 2003 O'Reilly Media, Inc. All Rights Reserved. | ![]() |
| This content is excerpted from the above-named O'Reilly publication, with permission, by agreement with ActionScript.org. | |
Components are one of the cornerstones of Rich Internet Application (RIA) development using Flash and Flash Remoting. Components make it easy to create rich interfaces, but they can also be responsible for an application getting bogged down with poor performance. Many of the Macromedia user interface components are very code-intensive when it comes to doing things like populating the component, sorting, and adding data. You should be conservative in your use of client-side code when dealing with components. The DataGrid component, for example, can utilize 90-100% of the end user's system resources while being populated—from a remote service or local data.
TIP: The Flash 2004 component architecture is optimized for applications that use five or six components; the shared library that Flash 2004 components require adds more file size than is justified by using only one or two components.
The speed of a component is often dependent on the type of code you are utilizing. What looks more efficient to the ActionScript programmer is often slower in execution. If you're in doubt about the speed of a particular section of code, you can time it using the Date( ) object. The code in Example 12-2 is a CodeTimer object, which can be used to time the execution of sections of code.
To use the timer, simply start it by creating a new CodeTimer object at the start of the code you want to time, and call the CodeTimer.trace( ) method at the end of the code:
[as]// Initialize the timerThe preceding example tests the speed of the DataGlue.bindFormatStrings( ) method. The example recordset that I tested yielded an average result of 10 milliseconds. The following code shows the time taken to manually populate the same ComboBox with the same recordset using an index loop:
[as]var t = new CodeTimer("Testing index loop");The second example yielded an average result of 30 milliseconds. The last example uses a for...in loop and a much more concise coding style:
[as]var t = new CodeTimer("Testing for/in loop");The third example is less verbose and looks like it is more efficient, but it results in an elapsed time of 90 milliseconds—9 times slower than the first example—showing the efficiency of the DataGlue class. Looks are deceiving sometimes when coding, as this example demonstrates. It's best to not simply take the code at face value; measure your code's execution time and try different things to get the best possible results.
Shaving 80 milliseconds off the data-loading operation for one ComboBox can have a dramatic impact in the end user's experience if you have many user interface elements and can optimize some of them in similar ways. 80 milliseconds might not seem like a lot, but when you're dealing with a Flash interface it is wise to conserve where you can, to balance the initial load time of the movie, which might run into several seconds. 10 or 20 interface elements can increase this delay by 1 or 2 seconds for the user. Every little bit of optimization helps to improve the user experience.
Here is a simple technique for testing performance. When you're testing a brief operation, it may be so fast that you can't get an accurate measure of the time it takes. Furthermore, if an operation is performed hundreds or thousands of times during your program execution, the execution time may vary, and it isn't practical to add up the times of each execution manually. You can add a for...in loop to execute an operation, say, 100 times to give you a more accurate picture of the time required for execution. Here is the new code (additions shown in bold):
var t = new CodeTimer("Testing index loop 100 times");
for (var j; j < 100; j++) {
var fields = result_rs.getColumnNames( );
for (var i; i < result_rs.getLength( ); i++) {
cbName.addItemAt(i,
result_rs.getItemAt(i)[fields[1]],
result_rs.getItemAt(i)[fields[0]]);
}
}
// Displays total and average execution time
elapsedTime = t.trace( );
trace("Average Elapsed Time: " + elapsedTime/100 + " milliseconds");
Don't forget to take the average time, and don't forget to remove the for...in loop when you're done testing (you can comment out the testing code so that it is easy to reinstate if you want to test it again later). Be sure to test the operations that you are trying to optimize. How would the timing differ if the call to getColumnNames( ) were moved outside the second loop? Refer to "Looping" later in this chapter for hints on optimizing loop performance.
You can get much more elaborate with timing, but this simple example gives you the basic technique. Timing your code is very important when dealing with Flash Remoting or Flash interfaces in general. Sometimes, a call to the remote database will be quicker than trying to manipulate results on the client. Also, sometimes there are sections of code that execute too slowly in ActionScript and can be moved to the server.
Frequently, when building data-driven Flash interfaces, you test with a small amount of data with the knowledge that your application will grow in the future with more back-end data. The Flash UI components can generally handle a small amount of data, but when you start feeding thousands of records into them they start slowing down dramatically. How do you test with large amounts of sample data if the data doesn't exist yet?
One way is to use a temporary SQL statement in your server-side service that returns a lot more rows than your test data actually contains, by forcing a cross join on another table. A cross join gives you a result that contains every combination of rows of data from the joined tables. In other words, each row in the first table is joined to every row in the second table, giving you a huge resultset. A cross join has limited use in everyday data retrieval but is especially handy when you're testing application interfaces. Consider the following query on the Northwind database:
[code]SELECT c.CategoryID, c.CategoryNameThe query returns about eight rows of data. If you were to time this
data being rendered into a ComboBox or DataGrid, you would not be
able to discern any noticeable speed problem. Change the query to
cross join the Products table:
Now the query returns over 600 rows by joining the
Products table and returning a complete set of
data from the Categories table for each row of
data in the Products table. The fields that you
are retrieving are the same as the fields in the first example, but
they are duplicated many times. This gives you a better idea of what
your final application will be able to handle and gives you a more
accurate picture of where your bottlenecks are. (It also gives you a
chance to test your screen layouts to see if they accommodate large
recordsets and test your logic for pageable recordsets, if
applicable.)
UI components can greatly speed up the development process, but they can also be a performance bottleneck if the code that uses the component is not optimized.
Copyright © 2003 O'Reilly Media, Inc. All Rights Reserved. | ![]() |
| This content is excerpted from the above-named O'Reilly publication, with permission, by agreement with ActionScript.org. | |
Application programming interface (API) is a general term for how one block of code communicates with another. For example, each ActionScript class defines an API by making methods and properties available (or keeping them hidden). Keeping the API for your application clean, with well-defined properties, methods, and events, makes for fast programming by the rest of your team. A formal API helps to insulate one code module from changes in another module. Furthermore, updates to an application can be made more quickly if your properties, methods, and events are predesigned, documented, and consistent with the API that you set up.
Making your code reusable is one of the ways that you can improve the speed of coding in future applications. You should maintain stock objects and snippets of code that have been programmed, tested, and optimized in the past. For example, most applications have users, so if you maintain a standard User object that interacts with a User remote service, the two parts can be utilized over and over. There is no sense in reinventing the wheel each time. Even if an object or piece of code can't be used in its entirety, it can be used as a starting point. Developing a useful library of ActionScript or server-side code (or benefiting from libraries made available by others) can increase your productivity by orders of magnitude.
Keeping the program modular is not something that is confined to one style of programming; it is a concept that works in all cases. If you code is self-contained in modules, you have several advantages over code that is nonmodular:
Code in a module can be tested without relying on other parts of the application, by simply knowing what the code requires and what it returns.
Modules can be improved and replaced without affecting other elements of the application, assuming your new module does the same thing with the same properties, methods, and events associated with the old module (i.e., as long as the API to the module doesn't change).
A test module can be plugged into the application in place of the real module, to facilitate easy testing.
Other programmers who might need to work on the application can understand the code more easily.
Having modularized code does not simply mean separating the code into sections. I worked on an application once, built by a fellow programmer, that was broken down into several include files. At first glance, it seemed logical that the program was broken down as it was. However, after diving into the code, I could see that each file had elements that relied on other files (variables, functions, etc.), which made it very hard to update the application. In the end, I wasted more time trying to figure out what went where than the time it would have taken to rewrite the code using a more logical approach. Therefore, trying to maintain modularity can help alert you to poor application design. If you can't modularize your code, you probably need to redesign it, even if it means starting over.
Copyright © 2003 O'Reilly Media, Inc. All Rights Reserved. | ![]() |
| This content is excerpted from the above-named O'Reilly publication, with permission, by agreement with ActionScript.org. | |
Most of your performance problems are going to be the result of a repeated operation that is not optimized. A nonoptimized piece of code that executes one time is not likely to have an impact on an application; however a nonoptimized piece of code that executes 1,000 times in a loop is going to kill the performance. For that reason, any code that is executed repeatedly, such as an onEnterFrame( ) handler or a for loop is a potential bottleneck.
Macromedia improved the performance of the Flash Remoting classes in the first updater, available at http://www.macromedia.com. One technique that was originally used throughout the Flash Remoting classes involves initializing a for loop in a nonoptimized way. In standard ECMA scripting, the middle section of the for loop (the condition in the for(variable; condition; update) construct) is evaluated each time the loop executes. For that reason, a static value should be used whenever possible.
Bad:
[as]for (var i = 0; i < this.getLength( ); i++) {Good:
[as]var tempLength = this.getLength( );However, counting down is even faster:
[as]var tempLength = this.getLength( )-1;Optimizing a loop's condition
expression is only the first step. Code within the loop should be
optimized whenever possible, for this is where your application
performance is going to suffer the worst. This is especially true if
you are populating a UI component, such as a Tree. If a piece of code
that takes 1 millisecond to execute can be removed from a loop that
is repeated 1,000 times, you'll save 1 second (which
can make an application feel much more responsive). The most basic
technique is moving outside the loop any operation that
doesn't need to be performed repeatedly. Consider
the following two cases. In this case, the string is initialized to
"Hello Tom!" within the loop:
In this case, the string is initialized outside the loop:
[as]myString = "Hello Tom!";In the first case, the string is initialized repeatedly, even though
its value never changes. The second loop will execute faster (about
1.8 milliseconds versus 2 milliseconds). You might look for other
ways to optimize the code. For example, the trace( )
statement, because it displays text in the Output window,
might slow things down. Here, we accumulate all the text in a
variable, outputString, and display it once,
rather than executing the trace( ) statement
repeatedly:
Surprise! This version takes an average of 2.4 milliseconds and is therefore slower than the trace( ) version. It turns out that string concatenation is a more expensive operation than tracing the text in the Output window.
Loops should also be terminated (broken out of) as soon as possible. Take the following piece of code as an example:
[as]var temp = myArray.length;The preceding example sets a flag when a specific item has been found in the array. The problem is that the loop continues even after the item has been found. This is wasteful, especially in a recursive loop that might be populating a UI component. A better solution is to break out of the loop as soon as the item is found:
[as]var temp = a.length;A loop like this will run an average of 50% faster, because an item might be found at the beginning of the loop or the end of the loop but the average will be somewhere in the middle. The loop is ended as soon as the item is found.
TIP: The preceding techniques apply equally well to any code that is executed repeatedly. For example, don't initialize data within an event handler that is called repeatedly. Initialize it once outside the event handler and refer to it as needed. Likewise, once an event handler is no longer needed, you should cease executing it. For example, if you've added a listener with addListener( ), you can remove it with removeListener( ) when it is no longer needed.
Copyright © 2003 O'Reilly Media, Inc. All Rights Reserved. | ![]() |
| This content is excerpted from the above-named O'Reilly publication, with permission, by agreement with ActionScript.org. | |
There are at least two different approaches to programming in ActionScript 1.0: object-oriented programming and procedural programming. Both have their strengths and weaknesses. You could add a third group of programmers as well: those who program procedurally and use OOP concepts in their applications. This section will show some ways of doing things with Flash Remoting using these approaches.
Procedural programming, also known as top-down programming, uses techniques that have been around since the beginning of computer programming. With procedural programming, you write code from beginning to end and call functions when they're needed. Assembly language is an example of procedural programming. There is nothing inherently wrong with procedural programming, yet it has fallen out of favor with the advent of OOP.
Procedural programming focuses on the tasks. Using an example of the Products database from the earlier chapters, a procedural program asks the question "what has to be done?" and then proceeds to do it. For example, the code might follow like this (in pseudocode):
Each section of the program (addProduct, searchProducts( ), etc.) would contain more code that executes sequentially, with conditional logic to branch off into other areas of the program.
ActionScript 1.0 promotes the use of procedural programming by the very nature of the ECMA-262 specification. ECMA-262 is not a true object-oriented specification, but it does allow for OOP. It's a very loose language in that it does not require entry points, strict datatyping, class definitions, or even variable declaration. That does not make procedural programming bad; it just means you have to structure your code to make it modular and maintain organization as you do so. One programming flaw in a program can have consequences further down the line. Because the code is executed sequentially, each line of code depends on what comes before it.
TIP: ActionScript 2.0, based on ECMAScript 4 and supported in Flash 2004 and Flash Pro, is geared more toward object-oriented programming, requiring strict typing, formal class declarations, and other constructs familiar to Java programmers. However, ActionScript 1.0 (the version supported by Flash MX) is still supported in Flash 2004 and Flash Pro. ActionScript 1.0 is not strictly case-sensitive in Flash Player 6. However, when exporting for Flash Player 7, ActionScript 1.0 is strictly case-sensitive, as is ActionScript 2.0.
Flash also operates as an event-driven application, and event-driven applications are procedurally oriented. When the movie loads, all of the code in the movie is executed (depending on the timeline, of course). Flash then waits for user input. The user input triggers events that can be trapped with event handlers. These event handlers become named functions when you're using procedural programming:
[as]myButton_pb.setClickHandler("getProducts");When you're using procedural programming in a Flash Remoting application, it becomes even more important to keep the code structured and clean. A procedural program can quickly turn into spaghetti code if the program lacks structure and organization. That said, a procedural program can also be well-constructed and function perfectly.
When dealing with remote services, you have several choices in how you handle the results. The simplest and most documented way of retrieving results is to name a function using the remote method name with an appended _Result or _Status. Generally, a procedural approach would utilize this method:
[as]myService.loginUser(user_txt.text, pwd_txt.text);This method is simple, direct, and effective. It is self-documenting, because the remote method name is used in the naming of the callback function. However, it does become cumbersome when dealing with many remote calls. I would not discourage someone from using it, but I would not consider it a best practice. That said, there is nothing wrong with using this technique if you feel comfortable using it.
Example 12-3 is an example of a procedural program with structure.
The procedural style mixes the user interface logic (inside the
sayHello_Result( ) function) and is executed
from the top down. Events that are triggered (such as when the
submit_pb button is clicked) are handled by named
functions. Events returned by a remote service are handled by
functions, sayHello_Result( ) and
sayHello_Status( ), that are named after the
calling method.
A procedural program such as this can easily grow into spaghetti code
if you are not careful. Even in this simple example, the
results_txt field is referenced in several places.
If something were to change in the interface, you would have to find
all of your user interface references and change them manually.
A better option is to use a custom responder object, as discussed in Chapter 4. Some of the more flexible options are shown in Section 12.7.2.
Copyright © 2003 O'Reilly Media, Inc. All Rights Reserved. | ![]() |
| This content is excerpted from the above-named O'Reilly publication, with permission, by agreement with ActionScript.org. | |
Object-oriented programming (OOP) is at the opposite end of the programming spectrum from procedural programming. In true OOP, everything is an object. Code in the program does not exist if it is not part of an object. For that reason, Flash MX is not a true OOP environment; you don't have to create objects in order for the program to operate, although inline code is technically part of the current object where the code resides. Also, some of the key principles of OOP, such as data hiding (private, protected, and public members), are not implemented in ActionScript. Even though strict OOP is not entirely possible with Flash MX, you can get pretty darn close by simply using OOP principles in your coding style.
TIP: Flash 2004 and Flash Pro support ActionScript 2.0, which is much closer to a true OOP language than ActionScript 1.0. The following discussion applies whether you're using ActionScript 2.0 or trying to stretch ActionScript 1.0 to act as if it were truly object-oriented, although ActionScript 2.0 enforces stricter coding requirements.
With OOP, you will want to create objects for everything. The application itself is an object; the user of the application is an object; every button on the screen is an object; the connection to the remote server is an object; the user's email address can be an object. An object is an instance of a class. A class is the coded blueprint for an object. Imagine the classes as rubber stamps, and the objects as the imprints you make when you use each rubber stamp. How you organize your classes and tie them together is one of the keys to understanding how OOP works.
OOP works in the exact opposite way that procedural programming works. In procedural programming, you ask yourself "What has to be done?" and then you do it. In OOP, you create abstract representations of each item in your application and ask yourself "How do they communicate?" Each class is created as a black box; you know what it does, you know what it needs, and you know what it returns. You don't have to know how it works, and you can remove it and substitute another black box with the same properties, methods, and events and the program will still work. Your class encapsulates the functionality and allows other classes to interact with it.
In Flash Remoting, there are several different ways you can encapsulate the functionality in objects:
A broadcaster is another object that you can use to simplify how remote method calls are handled on the client. When you use a broadcaster, you also have a listener. When the broadcaster broadcasts an event, the listener is automatically informed and performs some function. This is ideal for Flash Remoting, where a remote method does not provide an immediate response, but rather sends an onResult event back to the movie.
The Model/View/Controller (MVC) design pattern allows you to separate functionality into distinct units. These patterns have been utilized by many Flash Remoting applications. The Model is the business object, handling the logic of the application; the View is the unit that handles the UI, such as the text fields, buttons, and UI components; and the Controller is the catalyst between the Model and the View, handling communication between them. In a Flash Remoting application, the Model is usually split between ActionScript on the client and the server-side services.
There are several ways to implement OOP in Flash. Generally, the more abstract you make your classes, the easier the classes will be to understand for other programmers. I mean abstract in the sense of "evoking something's distilled essence," not "esoteric and obtuse." You should create classes that represent something meaningful. For example, your class should not be called RemoteService with methods that merely mirror your remote methods. This is obtuse and redundant, not abstract; it merely serves as a convenient way of accessing your services. An abstract class would be called Product, User, EmailAddress, or Search. These are human-readable objects that represent something meaningful to the application.
Objects are typically modeled before a line of code is written. Modeling involves identifying the objects in your application and documenting how they communicate via the properties, methods, and events of each object. Modeling can be done in many ways: using a Universal Modeling Language (UML) diagram, 3 × 5 cards (one for each object), or plotted on paper. In an OOP application, the more modeling you do in advance of coding, the easier it will be to create the objects and complete the coding successfully. In Flash Remoting, you must identify how an object will receive the remote result and how it will handle the result using an OOP mentality.
Throughout the book, I've shown a technique that makes sense in many situations—utilizing a custom responder object, like this:
[as]function LoginResponder( ) {or this:
[as]function LoginResponder ( ) {A better technique, however, is to use a callback function or a broadcaster within the responder object. The previous code is tied to the user interface, which is not an object-oriented approach; the user interface elements are not separate from the LoginResponder object. If you pass a callback function to the object, the LoginResponder is separate from the UI. You might start with a Responder class:
[as]function Responder ( ) {}Then, create a LoginResponder class for specific functionality:
[as]// LoginResponder extends ResponderThe preceding LoginResponder class defines a responder object that uses the callback function passed to it. You can use it from another class designed to gather information from the UI:
[as]myUserObject = new UserObject( );Inside the UserObject class you would have a loginUser( ) method, which would call the remote service:
[as]#include "LoginResponder.as"
Copyright © 2003 O'Reilly Media, Inc. All Rights Reserved. | ![]() |
| This content is excerpted from the above-named O'Reilly publication, with permission, by agreement with ActionScript.org. | |
There are a few inconsistencies with Flash Remoting when working with objects. The asynchronous nature of Flash Remoting makes it difficult to create objects that separate UI and content from your remote results. Because the results are accessed within an onResult( ) method, you might be tempted to access interface elements from within the same method. This would break the principle of encapsulation, which basically says that objects should behave as black boxes. In a properly encapsulated object, the internal workings of the object don't rely on external items such as UI elements. You can overcome the obstacle by using a broadcaster inside the onResult( ) and onStatus( ) methods, or by passing a callback method to the object, which would be called inside of onResult( ) or onStatus( ), as we'll see shortly.
Another problem involves having a service object as part of a custom object that is sent to the server in a remote method. It is a natural tendency to want to encapsulate the object to be self-sufficient and exist as a unit. One way to do that is to have your remote service as a property of the object. Unfortunately, this causes the remote call to fail due to an internal fault with Flash Remoting. The Macromedia Pet Market blueprint application (http://www.macromedia.com/devnet/mx/blueprint) suffers from this problem, but the programmers worked around the issue by copying the object properties to another object before calling the remote service. Workarounds such as these are commonplace, as Flash Remoting is still in its infancy and has a few kinks to work out.
This section demonstrates an example that uses callback functions and shows how the procedural code from Example 12-3 might be implemented as an OOP application. There are a few extra steps involved in turning a simple example into a full-fledged OOP application. You'll have to start with a new movie named HelloUserOOP.fla and follow these steps (the completed file is available at the online Code Depot):
Add 2 layers to the timeline: actions and ui.
Create a new MovieClip using Insert ? New Symbol.

Figure 12-1. Creating a new MovieClip for HelloUser
Inside the symbol, rename layer Layer1 to actions.
Define a HelloUserClass class in the first frame of the actions layer, as follows. This class initializes the gateway and is the basis of our application:
[as]#initclipNavigate back to the main movie and enter the following ActionScript
into the actions layer of the main timeline.
Notice the include file, User.as, which is a
custom class that we'll set up for this application.
The initialization code shown creates a
NetConnection object upon loading (through the
HelloUserClass), and creates a
User object. From this code, you can see that
the User object instance is calling three
different methods: setService( ),
setName( ), and sayHello(
). The submit_pb button has a private
method, displayMessage( ), within the anonymous
function built for the onRelease event. A
reference to displayMessage( ) is passed to the
User object as a callback function:
Create the User class. The User.as file should be saved in your Flash configuration directory under Configuration\Include\com\oreilly\frdg. Our class files are using the same naming convention as the server-side services that have been used throughout the book. Alternatively, you can save the class files in the same directory as your .fla file—using the same subdirectories, com\oreilly\frdg. The User.as file contains the code shown here:
[as]/*The User class is extremely simple, with one argument in the constructor; two properties, each with getter/setter methods; and one public method that is used as an interface to the remote method, sayHello( ). The User class exists apart from the user interface code set up previously. The Flash UI that was set up will work with any User class that we implement in the future, as long as the API to the class remains the same (i.e., the same public properties, methods, and events).
You can see that the User class also requires one other class: the Result class. Create the Result.as file as follows, and save it to the same directory as the User.as file:
[as]/*The Result class is a special responder object. The responder does not act on any of the results, and, as such, it can be used for any remote service call. You pass a callback function to the instance of the Result class when you instantiate it. In this case, we instantiated the object in the User object instance defined in Step 7:
[as]User.prototype.sayHello = function (callback, errorHandler) {Save and test the movie. It should work exactly as the procedural example.
Objects communicating: that's what OOP is all about. The user interface knows nothing of the Result class. It knows only about the User object and how to communicate with it. It depends on the User object; however, any User object that provides the same properties, methods, and events could be substituted without a problem. You'll notice that the OOP code is much wordier than the simple procedural example that does the same thing. Even so, the initial time spent modeling your application and setting up your classes is regained when you implement the application and make modifications further down the road. Modifications come easy to an OOP application.
Copyright © 2003 O'Reilly Media, Inc. All Rights Reserved. | ![]() |
| This content is excerpted from the above-named O'Reilly publication, with permission, by agreement with ActionScript.org. | |
A broadcaster is based on the Observer pattern, another standard design pattern in programming. A broadcaster is implemented in ActionScript using the undocumented ASBroadcaster class. With this class, you can create objects that broadcast custom events inside your movie. After an event is broadcast, a listener that is listening for that particular event will respond.
WARNING ASBroadcaster is an undocumented class, and, as such, it may not remain in the language forever. You can implement the example here using ASBroadcaster or one of the numerous substitute broadcasters freely available on the Web.
Broadcasters fit right into the Flash Remoting framework because of the asynchronous nature of the technology. When you call a remote service, you don't wait for the response. The remote service method eventually returns a result to the responder function in the Flash client. The remote service is essentially a broadcaster, and your responder object is essentially a listener. This does not provide enough flexibility in handling results, however, so it makes sense to set up a custom broadcaster to convey the remote response to the part of your Flash movie that will benefit from it.
You can set up a broadcaster inside of your responder to broadcast a custom event to the movie. The advantage of this approach is that, once the event is broadcast, you can have one or more listeners acting on the remote response. To create a broadcaster, pass an instance of the generic Object class to the static ASBroadcaster.initialize( ) method:
[as]var myBroadcaster = new Object( );This converts myBroadcaster into an
ASBroadcaster object capable of broadcasting.
Specify the custom event to broadcast using the
broadcastMessage( ) method:
Finally, set up a listener object to listen for the custom event.
Here, we create an object, myListener, with an
anonymous function assigned to the onMyCustomEvent
property:
Finally, add the listener to the object to
myBroadcaster using the addListener(
) method:
Example 12-4 utilizes a broadcaster to broadcast the onResult event from the server, rather than using a callback function. It uses the same HelloUserClass class as shown earlier in HelloUserOOP.fla, with no changes. The only changes are in the ActionScript code in the movie, as well as the two classes that were set up.
Create a copy of the User.as file and rename it UserBroadcaster.as. Change the constructor and the sayHello( ) method as show in Example 12-4 (changes shown in bold).
/*
User class
* public UserBroadcaster
constructor:
new UserBroadcaster( ); // Default user with no arguments
new UserBroadcaster(name); // Set a default name property
arguments:
name: string
properties:
service: the remote service with which the user interacts
name: the name of the user
methods:
getName: retrieve name property
setName: set name property
arguments:
name: string
getService: retrieve service object
setService: set the remote service for the object
arguments:
connection: a NetConnection object
servicePath: a path to a remote service
sayHello:interface to remote method, sayHello( )
arguments:
none
Dependencies:
com.oreilly.frdg.BroadcasterResponder
*/
#include "com/oreilly/frdg/BroadcasterResponder.as"
function UserBroadcaster (name) {
if (arguments)
this.name = name;
// Set this class up as a broadcaster
ASBroadcaster.initialize(this);
}
UserBroadcaster.prototype.getName = function ( ) {
return this.name;
};
// Set the name property only if the argument exists and is not blank
UserBroadcaster.prototype.setName = function (name) {
if (name != "" && name != undefined)
this.name = name;
};
UserBroadcaster.prototype.getService = function ( ) {
return this.service;
};
// Create remote service object as a property of User
UserBroadcaster.prototype.setService = function (connection, servicePath) {
this.service = connection.getService(servicePath);
};
UserBroadcaster.prototype.sayHello = function ( ) {
this.getService( ).sayHello(new BroadcasterResponder("onSayHello", this),
this.name);
};
Let's compare the
UserBroadcaster class in Example 12-4 with the User class from
the earlier callback implementation. The main differences are the
initialization of the class as an ASBroadcaster
in the constructor and the fact that the sayHello(
) method now uses a different responder object:
BroadcasterResponder. You pass a custom event
("onSayHello") and the broadcaster object
(this) to the responder function. The responder
object notifies any listeners. The
BroadcasterResponder responder
function's definition is shown here:
The BroadcasterResponder function accepts two
arguments: the custom event that will fire when
this responder is called, and the broadcaster that
will broadcast the message (the UserBroadcaster
object instance, in this case). The implementation is simple: when a
successful result is returned from the server, the
onResult( ) method is called and the broadcaster
broadcasts the event ("onSayHello" in this case)
and the actual results from the remote call to the movie. If an error
is received by the onStatus( ) event handler,
the name of the event becomes event+ "Error", or
"onSayHelloError" in this case. Next, listeners
need to be set up in the main movie:
The listener object is the results_txt TextField.
Any object can serve as a listener, but the object must have a
function set up to respond to your custom event. We simply create the
necessary event handlers on the object (by setting the
onSayHello and onSayHelloError
properties to anonymous functions) and then add it as a listener to
receive events fired off by the UserBroadcaster
instance (app.myUser).
Again, this technique is well suited to Flash Remoting. The Macromedia Pet Market blueprint application also uses custom broadcasters. One advantage, as mentioned earlier, is that you can add multiple listeners to the event. For example, you can add this code to create a built-in debugging listener:
[as]var debug = true;The listener is "turned on" when
the debug flag is set to true. Doing this, you can
add listeners to all of your remote calls without having to dig into
your code to make changes and put trace( )
statements all over the place. It can all be done from one place,
because your listener is listening for the event.
Another common way to build an application is to mix procedural style with some OOP concepts. ActionScript 1.0 makes it easy to program in this way by not forcing the rules of OOP on you, as some other languages, such as ActionScript 2.0, require. The procedural example shown earlier could easily benefit from some of the techniques shown in the sections about OOP. For example, the code could implement callback functions in a custom responder object or a broadcaster. Chapter 14 shows a complete Flash MX application that is built procedurally using OOP concepts.
Copyright © 2003 O'Reilly Media, Inc. All Rights Reserved. | ![]() |
| This content is excerpted from the above-named O'Reilly publication, with permission, by agreement with ActionScript.org. | |
ColdFusion programmers have a construct built into the RecordSet class on the client and server that can reduce the time it takes for the data to display. Chapter 5 went into detail about the technique of RecordSet paging. In many cases, this technique improves the apparent performance of your application; your users will see results on the screen even before the download of the entire recordset is complete. Improving the apparent performance of your application can often have as much of an impact on the end user as improving the actual performance of the application. Your user can judge the application only by what he sees.
Caching involves maintaining a piece of information in a store or cache to improve performance of an application. In Flash Remoting, you can create objects to hold the contents of a remote service call. This can be handy in many situations:
An email program that retrieves email messages from a remote server, allowing you to read the messages by choosing a message header in a list. Upon moving to another message, the current message could be cached, so that if you were to return to that message, it would not have to be retrieved from the server again.
A product listing that has master/detail pages of a product list. As you choose a product, the details page retrieves the product details from the remote server. Upon choosing another product, the current product is placed into a cache for easy access when the user returns to that product.
A book review application, where a book review is retrieved from a remote service. As the user chooses another book, the current review is stored in a cache, in case the user returns to it. Each subsequent viewing of the book review comes from the cache rather than the remote service.
A cache is typically implemented as an object or an array of objects. Each object represents one item from the remote service. All items are not retrieved, but the cache is indexed in a way that each item that is placed in the cache can be easily retrieved, as in the following code snippet:
[as]// Set up the custom object that holds the product informationThe complete functionality of the preceding example is not implemented here, but similar functionality can be found in the application built in Chapter 14. When a user clicks an item in the Tree component, the displayProduct( ) function fires off. We use a helper function, findItem( ), to pick the item out of the cache if the item exists. If so, the displayCacheItem( ) function displays the item directly from the cache, rather than going to the remote service. If the item is not found, another function is called (putProductInCacheAndDisplayIt( )), which puts the current item into the cache before displaying it.
Copyright © 2003 O'Reilly Media, Inc. All Rights Reserved. | ![]() |
| This content is excerpted from the above-named O'Reilly publication, with permission, by agreement with ActionScript.org. | |
I've isolated the sections in this chapter into 10 items that you should remember when building your Flash Remoting applications:
Clearly separate the tasks for the application.
Clearly separate and optimize the functionality between client, server, and database.
Handle server downtime (lack of a connection) gracefully.
Use components wisely.
Maintain a clean API.
Optimize your loops and other code blocks that are executed repeatedly.
Use OOP or OOP concepts when possible.
Use broadcasters or callback functions in your responder objects.
Take advantage of incremental recordsets in ColdFusion.
Cache objects from the server whenever possible—on the client
using a cache object or on the server using
cachedwithin in ColdFusion or whatever caching
functionality is at your disposal.
In this chapter, you learned some new techniques that will improve your Flash Remoting application development. Following best practices in your applications can make both the time you spend coding and the time your user spends browsing much more productive. For more information on best coding practices, see the resources cited in Appendix B.