12.7 OOP or Not OOP

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.

12.7.1 Procedural Programming

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.

12.7.1.1 Task-oriented

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):

1. Initialize movie
2. Call remote methods to populate UI
3. Display results
4. Wait for user input
5. If "add" is clicked, show the addProduct screen
6. If "search" is clicked, call the remote method searchProducts( )

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.

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.

12.7.1.2 Event-driven

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:

myButton_pb.setClickHandler("getProducts");
function getProducts( ) {
  myService.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.

12.7.1.3 Result handlers in procedural programming

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:

myService.loginUser(user_txt.text, pwd_txt.text);
loginUser_Result = function (result) {
  if (result == true) {
    trace("User logged");
  } else {
    trace("User not logged");
  }
};

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.

12.7.1.4 Procedural example

Example 12-3 is an example of a procedural program with structure.

Example 12-3. A procedural approach to the HelloUser program
#include "NetServices.as"
// Set up variables for the URL and service paths
var myURL = "http://localhost/flashservices/gateway";
var servicePath = "com.oreilly.frdg.HelloUser";

// Connection hasn't been initialized; create connection and service objects
if (initialized == null) {
  initialized = true;
  NetServices.setDefaultGatewayURL(myURL);
  var myConnection_conn = NetServices.createGatewayConnection( );
  var service = myConnection_conn.getService(servicePath, this);
}
// Set up the callback function to handle mouseclicks
submit_pb.setClickHandler("callSayHello");

// Call the service when the user clicks the Submit button
function callSayHello ( ) {
  var user_name = userName_txt.text;
  if (user_name == "") {
    user_name = "User";
  }
  service.sayHello(user_name);
}

// Set up onResult and onStatus event handlers
function sayHello_Result (myResults) {
  results_txt.text = myResults;
}

function sayHello_Status (myError) {
  results_txt.text = myError.description;
}
// Set the system status to be handled by the method status handler as well
System.onStatus = sayHello_Status;

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.

12.7.2 Object-Oriented Programming

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.

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.

12.7.2.1 Everything is an object

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.

12.7.2.2 OOP in Flash Remoting

In Flash Remoting, there are several different ways you can encapsulate the functionality in objects:

Enclose your remote server calls in an object

Every remote method is mirrored in a method of an object in your Flash movie. For example, you might have a Products database, as was shown in Chapter 3. You would have a Product class and a ProductList class, which would be a collection of Product objects. The ProductList class might have a method called getList( ) that would retrieve the entire product list from the remote server. The Product class might have a method called addProduct( ) that would call an insert routine on the remote server to insert the product into the database.

Use broadcasters

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.

Use a Model/View/Controller (or Model/View/Presenter) architecture

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.

12.7.2.3 How to create your objects

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 x 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.

12.7.2.4 Responder objects in OOP

Throughout the book, I've shown a technique that makes sense in many situations?utilizing a custom responder object, like this:

function LoginResponder( ) {
  this.onResult = function (result) {
    if (result == true) {
      message_mc.message_txt.text = "User logged";
    } else {
      message_mc.message_txt.text = "User not logged";
    }
  };
  this.onStatus = function (error) {
    trace(error.description);
  };
}
myService.loginUser(new LoginResponder( ), user_txt.text, pwd_txt.text);

or this:

function LoginResponder ( ) {
}
LoginResponder.prototype.onResult = function (result) {
  if (result == true) {
    message_mc.message_txt.text = "User logged";
  } else {
    message_mc.message_txt.text = "User not logged";
  }
};
LoginResponder.prototype.onStatus = function (error) {
    trace(error.description);
};
myService.loginUser(new LoginResponder( ), user_txt.text, pwd_txt.text);

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:

function Responder ( ) {}
Responder.prototype.onResult = function (results) {trace(results);};
Responder.prototype.onStatus = function (error) {trace(error.description);};

Then, create a LoginResponder class for specific functionality:

// LoginResponder extends Responder
#include "Responder.as"
function LoginResponder (myCallback) {
  this.prototype = new Responder( );
  this.callback = myCallback;
}

LoginResponder.prototype.onResult = function (result) {
  if (result == true) {
    this.callback("User logged", result);
  } else {
    this.callback("User not logged", result);
  }
};
doMessage = function (message) {
  message_mc.message_txt.text = message
};

The 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:

myUserObject = new UserObject( );
myUserObject.loginUser("doMessage", user_txt.text, pwd_txt.text);

Inside the UserObject class you would have a loginUser( ) method, which would call the remote service:

#include "LoginResponder.as"
UserObject.prototype.loginUser = function (callback, username, password) {
  this.service.loginUser(new LoginResponder(callback), username, password);
};
12.7.2.5 Problems with OOP

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.

12.7.2.6 Callback example

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):

  1. Add 2 layers to the timeline: actions and ui.

  2. In the ui layer, create the user interface that was shown in Chapter 2, with an input TextField named userName_txt, a dynamic TextField named results_txt, and a PushButton component named submit_pb. There is also one static TextField that contains the text "Enter your name".

  3. Create a new MovieClip using Insert New Symbol.

  4. The dialog box that appears prompts you for a name. Name the symbol HelloUserClass, select the Export for ActionScript checkbox, as shown in Figure 12-1, and click OK.

Figure 12-1. Creating a new MovieClip for HelloUser
figs/frdg_1201.gif
  1. Inside the symbol, rename layer Layer1 to actions.

  2. 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:

    #initclip
    #include "NetServices.as"
    
    function HelloUserClass (url) {
      this.init(url);
    }
    
    Object.registerClass("HelloUserSymbol", HelloUserClass);
    
    HelloUserClass.prototype.init = function (url) {
      this.testingUrl = url;
      NetServices.setDefaultGatewayURL(this.testingUrl);
      this._conn= NetServices.createGatewayConnection( );
    };
    
    #endinitclip

    Navigate 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:

    #include "com/oreilly/frdg/User.as"
    
    // Call the service when the user clicks the Submit button.
    if (initialized == undefined) {
      initialized = true;
      _global.app = new HelloUserClass("http://localhost/flashservices/gateway");
      var servicePath = "com.oreilly.frdg.HelloUser";
      app.myUser = new User("User");
      app.myUser.setService(app._conn, servicePath);
    }
    
    submit_pb.onRelease = function ( ) {
      displayMessage = function (message) {
        results_txt.text = message;
      }
      app.myUser.setName(userName_txt.text);
      app.myUser.sayHello(displayMessage, displayMessage);
    };
  3. 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:

    /*
    User class
    
    public User
      constructor:
        new User( );     // Default user with no arguments
        new User(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:
                 callback: function to handle results of the remote call
            Dependencies:
              com.oreilly.frdg.Result
    */
    #include "com/oreilly/frdg/Result.as"
    
    // Constructor takes one optional argument (name)
    function User (name) {
      if (arguments)
        this.name = name;
    }
    
    User.prototype.getName = function ( ) {
      return this.name;
    };
    
    // Set the name property only if the argument exists and is not blank
    User.prototype.setName = function (name) {
      if (name != "" && name != undefined)
        this.name = name;
    };
    
    User.prototype.getService = function ( ) {
      return this.service;
    };
    
    // Create remote service object as a property of User
    User.prototype.setService = function (connection, servicePath) {
      this.service = connection.getService(servicePath);
    };
    
    // Interface to remote method, sayHello( )
    User.prototype.sayHello = function (callback, errorHandler) {
      this.getService( ).sayHello(new Result(callback, errorHandler), this.name);
    };

    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).

  4. 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:

    /*
    public Result
      constructor:
        new Result(resultHandler, errorHandler); // Set result handler and error
                                                    handler properties
          arguments:
            resultHandler: function
            errorHandler: function (optional)
          properties:
            none
          methods:
            onResult: method to handle remote results
               arguments:
                 myResult: argument returned from remote call
            onStatus: method to handle remote errors
               arguments:
                 myError: argument returned from remote call in event of error
    Dependencies:
        none
    */
    
    function Result (resultHandler, errorHandler) {
      this.resultHandler = resultHandler;
      this.errorHandler = errorHandler;
    }
    // Set up onResult( ) and onStatus( ) handlers as methods of the Result class
    Result.prototype.onResult = function (myResults) {
      this.resultHandler(myResults);
    };
    
    Result.prototype.onStatus = function (myError) {
      if (this.errorHandler == "undefined") {
          trace(myError.description);
      } else {
          this.errorHandler(myError.description);
      }
    };
    System.onStatus = Result.prototype.onStatus;

    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:

    User.prototype.sayHello = function (callback, errorHandler) {
      this.getService( ).sayHello(new Result(callback, errorHandler), this.name);
    };
  5. 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.

12.7.2.7 Broadcasters

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.

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:

var myBroadcaster = new Object( );
ASBroadcaster.initialize(myBroadcaster);

This converts myBroadcaster into an ASBroadcaster object capable of broadcasting. Specify the custom event to broadcast using the broadcastMessage( ) method:

myBroadcaster.broadcastMessage("onMyCustomEvent", "Hello there");

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:

myListener = {
  onMyCustomEvent:function(message) {
    trace(message);
  }
}

Finally, add the listener to the object to myBroadcaster using the addListener( ) method:

myBroadcaster.addListener(myListener);

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).

Example 12-4. UserBroadcaster class
/*
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:

/*
public BroadcasterResponder
  constructor:
    new BroadcasterResponder(event);
      arguments:
        event: the event that will be broadcast
      properties:
        none
      methods:
        onResult: method to handle remote results
           arguments:
             event: the event that will be broadcast
        onStatus: method to handle remote errors
           arguments:
             event: the event that the error occurred in
Dependencies:
    none
*/

function BroadcasterResponder (event, broadcaster) {
  this.event = event;
  this.broadcaster = broadcaster;
}
// Set up onResult( ) and onStatus( ) handlers as
// methods of the BroadcasterResponder class
BroadcasterResponder.prototype.onResult = function (myResults) {
  this.broadcaster.broadcastMessage(this.event, myResults);
};
BroadcasterResponder.prototype.onStatus = function (myError) {
  this.broadcaster.broadcastMessage(this.event + 'Error', myError);
};
System.onStatus = BroadcasterResponder.prototype.onStatus;

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:

#include "com/oreilly/frdg/UserBroadcaster.as"

if (initialized == undefined) {
  initialized = true;
  _global.app = new HelloUserClass("http://localhost/flashservices/gateway");
  var servicePath = "com.oreilly.frdg.HelloUser";
  app.myUser = new UserBroadcaster("User");
  app.myUser.setService(app._conn, servicePath);
}

submit_pb.onRelease = function ( ) {
  app.myUser.setName(userName_txt.text);
  app.myUser.sayHello( );
};

// Listener object for the onSayHello event
results_txt.onSayHello = function (message) {
  this.text = message;
};

// Listener object for errors in onSayHello
results_txt.onSayHelloError = function (message) {
  this.text = message.description;
};

app.myUser.addListener(results_txt);

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:

var debug = true;
// var debug = false;  // Uncomment this line to turn off debugging
var debugListener = {onSayHello:function(message) {
  trace("User name: " + app.myUser.getName( ));
  trace("Results from server: " + message);
}}
if (debug) app.myUser.addListener(debugListener);

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.

12.7.3 Mixing Procedural and Object-Oriented Code

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.



    Part III: Advanced Flash Remoting
     
    ASPTreeView.com
     
    Evaluation has СЅНЅ¶ґїТѕјexpired.
    Info...