Introduction

Flash is a lightweight cross-platform runtime for rich media, enterprise applications and mobile applications, as well as an integrated development environment. Flash can be programmed in ActionScript 1/2/3.

Archive for the ‘Flash’ Category

Saturday, June 4th, 2005

As2lib Exception API

This tutorial covers exception basics, the key parts of the As2lib Exception API (org.as2lib.env.except) and how they improve your application and help you during development on your Flash application.

Note that this tutorial has also been published on www.as2lib.org: As2lib Exception API.

Exception Basics

What exactly is an exception and what problems does it solve? Which features make up a good exception API? What are good practices when working with exceptions and to what issues do I have to pay special attention to?

Programmers try to avoid the potential of errors as good as they can; but unfortunately errors nevertheless occur. Thus handling errors is an important issue when writing applications. If errors are properly handled you improve your program in readability, reliability and maintainability. But, to handle errors properly you need a good underlying error handling API. While the ActionScript 2.0 programming language provides needed key constructs like try..catch..finally-blocks and a throw-statement it lacks a good API.

The only help Macromedia’s Error API offers is a class “Error” which can be passed a message and which returns that message on an invocation of its “toString” method. This is definitely to less to make up a good error handling API. Missing features are:

  • A stack trace that tells at least who, which class and method, threw the error and which parameters were passed-to the method when it threw the error. In the best case, this stack trace contains the whole method execution sequence beginning at the method that threw the error and ending at the application’s entry point.
  • The ability to assign cause-errors. These are catched errors that themselves caused other errors to be thrown.
  • At least two types of exceptions that differ in fatality. The less fatal exception may be caused by a wrong user input and the fatal exception indicates a programming error.

These are the features of the As2lib Exception API. With the term exception we refer to an error or better an exceptional event. This means an exception is an event that occurs during the execution of an application that disrupts its normal flow. Note that this does not mean that the exception will break the application. The exception should and must be handled somewhere in the application before this happens. At best the application will then recover itself and at worst it prints an error message to the user. With fatal exceptions indicating programming errors you may also think about directly informing the developer about it.

When you use exceptions in your application you should pay special attention to the following issues:

  • Make your exception informative by providing a detailed message intended for developers that includes as much context information as possible and maybe even information on how to resolve the problem. You may also provide an error code either as string or number that can be used to obtain an error message to display to the user.
    If you want to include the content of a variable or method etc. specify its name surrounded by single quotation marks “‘”, followed by the string representation of its content surrounded by a squared open “[” and close “]” brace, for example “… ‘myVariable’ [itsContent] …”.
  • Create a base exception per module of you application or event per package. In an application these base exceptions are in most cases normal, not fatal, exceptions. They enable you to catch exceptions at different levels of detail.
  • Distinguish different problems not only by error messages and error codes but by separate sub-classes with an informative name of your common base classes. This is because you cannot catch exceptions based on error messages or error codes and thus prevent code to react appropriate if different reactions are required.

Now enogh of all that theory; let’s take a look at the As2lib Exception API.

The Throwable

How are the spoken about exception basics realized in the As2lib Exception API? How can I best use this API? To what must I pay attention to when working with exceptions in ActionScript 2.0?

The As2lib Exception API supplies two kinds of exceptions:

  • The “Exception” class that indicates a normal exceptional event that can in most cases be handled. Such an exception is for example the “UnknownServiceException” that is thrown if an external service cannot be reached. The application can in such a case retry to connect after waiting a few milliseconds.
  • The “FatalException” class that indicates a fatal exceptional event that is mostly caused due to a programming error and can mostly only be resolved by the programmer itself. A fatal exception is for example the “AbstractOperationException” that indicates that a method has been called that was marked as abstract and should have been overridden by the programmer.

Both these exceptions are implementations of the “Throwable” interface that is the core piece of the API. The most important methods this interface declares are:

  • The “initCause” method that initializes the cause of the new throwable. Sometimes you want or you have to transform a low level exception into a high level exception. You can do this by just catching the low level exception and throwing a new high level exception. But doing so without initializing the low level exception as the cause of the high level exception you would use all its useful information.
  • The “addStackTraceElement” method that adds a new element to the stack trace. Make use of this method at key points in your application to make it easier following the stack trace and tracking down the exception’s cause.

When you encounter that you need to throw an exception, first look whether such an exception already exists, if not create a new one with an informative name that extends your base exception.

  1. class org.myproject.mymodule.MyException extends MyBaseException {
  2.     public function MyException(message:String, scope, args:Array) {
  3.         super (message, scope, args);
  4.     }
  5. }
  6.  

You can now throw this exception at the appropriate place providing an informative message and other helpful information. Do not forget to document the exceptions that are thrown in your method documentation. Because otherwise it is not possible to know which exceptions are thrown besides browsing the code or encountering an exception at run-time.

  1. /**
  2. * …
  3. * @throws MyException if …
  4. */
  5. public function doSomething(Void):Void {
  6.     …
  7.     throw new MyException("This is an informative error message.", this, arguments);
  8.     ….
  9. }

The last two parameters are are not mandatory but it is good pratice to provide them; otherwise where the exception came from will not be included in the resulting string message.

When you catch the exception you must pay attention to a quirk in ActionScript 2.0 compiled by Flash (this quirk does not exist when you use the Motion-Twin ActionScript 2.0 Compiler or Flex). If you do not declare the fully qualified exception name as exception type in catch-blocks, exceptions of exactly this type will not be catched. Thus do always declare the fully qualified exception name.

  1. try {
  2.     myInstance.doSomething();
  3. } catch (myException:org.myproject.mymodule.MyException) {
  4.     // do something specially related to this special exception
  5. } catch (myBaseException:org.myproject.mymodule.MyBaseException) {
  6.     // do something general
  7. } finally {
  8.     // do something that must be done in every case
  9. }
  10.  

If you do not catch an exception or if you trace the string representation of an exception in the Flash Output Panel may appear something like this:

  1. org.myproject.mymodule.MyException: This is an informative error message.
  2.   at org.myproject.mymodule.MyClass.doSomething()
  3. Caused by: org.myproject.myothermodule.MyLowLevelException: This is a low level message.
  4.   at org.myproject.myothermodule.MyOtherClass.doSomethingDifferent(Number, String)
  5.  

The Error Message

What is an error message? Why are detailed error messages important? To what should I pay attention to when writing error messages? How can I provide detailed error messages that are generated lazily or how can I manage to generate detailed error messages only when they are needed?

Error messages or detail messages are provided with every exception. They describe in detail what the problem is, why it is a problem and maybe even how this problem may be solved; try to give as much context information as possible. These messages are inteded for developers, not for display to users of your application. If you want to display a message to users, provide an error code with your exception. This error code can either be a string or a number. The advantage of a string error code is that it may itself make sense to users. You can use this error code to get an error message intended for users from a XML or property file that fits the user’s locale.

You may wonder why error messages are so important. The answer is simple. When you work on a project there are probably at least two programmers involved that work on code and use each others’ code. With detailed error messages you can quickly fix the problem if it occurs, without having to ask the other developer what the problem may be and how it can be fixed. The developer who wrote the code you must use may also have left your company some time ago, so you are not even able to ask him. Another issue is when you need to use code you wrote maybe 2 years before and you encounter an error. In all these situations (and there are definitely more) you are more than thankful that yourself and your co-workers took the time to write detailed error messages that help you to fix problems in just a few minutes.

When you write error messages you should pay attention to the format. You should not use different error message formats in one application. Following are common error message standards.

  • Write full sentences that start with an upper case letter and end with a period. An error message can consist of multiple sentences and even multiple lines.
  • Put in-line API names like parameter names, variable names, method names etc. in single quotes “‘”.
  • Put in-line content of for example a variable in square brackets “[]”. Ensure that the string representation of the content is useful and not just something like “[type Function]” but “org.as2lib.data.holder.map.HashMap”. You may want to use the As2lib Reflection API to format the content.
  • Write error messages in simple present.
  • Write as precise as possible.

I suggested using reflections to look up API names or do some similar tasks. The problem at this is that it costs time even though the exception may be catched one method down in the stack and the detailed error message may not be needed at all and was thus generated in vain. If it is likely that the exception is catched, that is mostly with non-fatal exceptions, it is a good idea to generate detailed error messages lazily. With the As2lib Exception API you can therefor override the “doToString” or the “getMessage” method. Do not override the “toString” method because it does some more work than just generating the string representation of the exception; it also forwards the exception’s string representation to a logger of the As2lib Logging API if the exception was not catched. This more than helpful information would be lost otherwise. The “MethodCallRangeError” of the As2lib Mock Object API uses this method of error message generation.

  1. class org.as2lib.test.mock.MethodCallRangeError extends AssertionFailedError {
  2.     private var methodCalls:Array;
  3.     private var expectedMethodCallRanges:Array;
  4.     private var actualMethodCallRanges:Array;
  5.    
  6.     public function MethodCallRangeError(message:String, thrower, args:Array) {
  7.         super (message, thrower, args);
  8.         methodCalls = new Array();
  9.         expectedMethodCallRanges = new Array();
  10.         actualMethodCallRanges = new Array();
  11.     }
  12.  
  13.     public function addMethodCall(methodCall:MethodCall, expectedMethodCallRange:MethodCallRange, actualMethodCallRange:MethodCallRange):Void {
  14.         methodCalls.push(methodCall);
  15.         expectedMethodCallRanges.push(expectedMethodCallRange);
  16.         actualMethodCallRanges.push(actualMethodCallRange);
  17.     }
  18.  
  19.     public function doToString(Void):String {
  20.         var message:String = getMessage() + ":";
  21.         for (var i:Number = 0; i < methodCalls.length; i++) {
  22.             message += "\n  " + methodCalls[i] + ": expected: " + expectedMethodCallRanges[i] + ", actual: " + actualMethodCallRanges[i];
  23.         }
  24.         return message;
  25.     }
  26. }

As you can see al the detailed information that tooks some time to be constructed is only generated if needed. This also factors the error message generation out of the actual code and into the exception, where it belongs. Note that in the above example we have overridden the “doToString” method and not the “getMessage” method. We are thus actually working with two levels of error messages, one detailed that is generated lazily and is returned by the exception’s “toString” method and one not that detailed that is passed-in on construction and returned by the exception’s “getMessage” method. If you override the “getMessage” method, the “doToString” method is the default one that uses the stringifier that can be edited. In this case the stack trace and possible causes are by default also included in the resulting string on invocation of the “toString” method. Note that this is not the case in the above example.

Thursday, June 2nd, 2005

As2lib Logging API

This tutorial covers logging basics, the simple and complex features of the As2lib Logging API (org.as2lib.env.log) and the integration of other logging APIs and custom behavior.

This article has also been published on www.as2lib.org: As2lib Logging API.

Logging Basics

Why is logging important? Why is logging superior to tool debugging? What are the basic requirements of a logging API? How do I approach logging? What is good and what bad practice?

Code must be debugged. With small projects debugging is not much of a problem, but when it comes to larger ones it is a pain to track down bugs that are only recognizable at run-time. These bugs may depend on some previous state and the interaction of large application modules. To make debugging easier it is necessary to trace the application’s execution. In Flash this can be done using “trace”. The problem with “trace” is that it is very primitive. It does not allow for different levels of output nor for custom output devices (an exception right now is the Motion-Twin ActionScript 2.0 Compiler, that allows you to replace calls to “trace” by a custom method prior to compilation). What is needed is a general easy to use logging API that allows for different log levels and that can be easily expanded to support an endless amount of different output possibilities and that can easily integrate other logging APIs without compromising their special functionalities.

You may wonder, why not just use a debugging tool? There are some major differences with instrumentation and debugging.

  • Debugging sessions help you track down today’s bugs, but not tomorrow’s.
  • Debugging is time consuming. It may be quicker to step through a log file to search for the bug source.
  • Logging makes you think about your application.
  • Logging let’s you easily track down bugs that occured when a client worked with your program. You may not be able to reproduce the bug or to fix it if you had not logged.
  • Logging is more informative than debugging in that it provides detailed human written description on what is going on.
  • Logging gives you control on how and where to log. You are not bloating your log file with trivial information.

As already stated a logging API should fulfil some general requirements.

  • A simple API.
  • Simple configuration.
  • Several log levels or priorities, like debug, info, warning, error and fatal and the ability to choose which messages at which level to log.
  • The ability to query whether messages with a given level will be logged. This may improve performance because it prevents unnecessary construction of messages that will not be logged.
  • The ability to change message formatting.
  • The ability to change output devices.

Given these functionalities it is also important when and how to log.

  • Log heavily in important and error-prone code sections.
  • Modify and improve log messages whenever you think of it to be necessary, like with ambiguous or unclear messages.
  • Think about the correct log level for log messages. Log messages at the same level should be of the same level of detail.

The Logger

How to work with the core and most important interface “Logger”, what this interace offers you and what its advantages are.

“Logger” is the most important interface of the As2lib Logging API. It is the only interface you work with in your application code. All other classes and interfaces of the API just offer special configuration functionalities that are not visible to your application code; you only may use them for configuration on start-up.

That said, what exactly offers the “Logger” interface?

  • Generic methods to log at different levels. These methods are in ascending priority: “debug”, “info”, “warning”, “error” and “fatal”.
  • Generic methods to figure out whether messages at a given level will actually be logged. These methods, again in ascending priority, are: “isDebugEnabled”, “isInfoEnabled”, “isWarningEnabled”, “isErrorEnabled” and “isFatalEnabled”.

All these methods are generic because they are not bound to any underlying API. The log methods take any type of message as parameter and the query methods return primitive boolean values. How the messages will be logged or how the level is determined depends on the specific implementation. This general approach makes it very easy to switch between different implementations and gives you the ability to work with the logging API of another framework to savor all its special functionalities without tieing your logging code in your application to this logging API.

The As2lib itself offers broad support for different logging APIs and consoles. These include ASCB Library Logging API, LuminicBox Logging API, Flash Debug Panel Source from Bit-101, Alcon, Flashout and SWF Console from Music Theory. This support is offered by “Logger” implementations designed to work with the specific APIs and by “LogHandler” implementations which are discussed in a following tutorial page.

In your application code you obtain a logger through a general access point like your application context or config or from the “LogManager” of the As2lib Logging API. The “LogManager”s duty is simply holding a reference to a logger instance that shall be returned on a call to the static “LogManager.getLogger” method or holding a repository that is responsible for the actual creation or look-up of the logger instance to return. But again the application code is unaware of where the logger comes from.

In your application code you best use a logger as follows.

  1. var logger:Logger = getLogger();
  2. if (logger.isInfoEnabled()) {
  3.     logger.info("An informative message.");
  4. }

As you can see I am first checking whether the info message will actually be logged and only if this holds true I will actually log the message. In the above example this check may not be necessary because we are not concatenating strings or looking-up a class’s name using reflections, so this may not improve performance. But in most cases it really improves performance; thus I think of it to be good practice.

Note that also I used a string message in the above example, I could also have logged any kind of object. How the string representation is obtained from the object depends on the concrete “Logger” implementation. It may obtain it just by invoking the object’s “toString” method, but it may also iterate over the object’s variables and methods and log these. Using an object as message may also improve performance because for example the “toString” method of the object is only invoked if the message is actually going to be logged. This approach could be called “Lazy Log Message Generation”.

There is one method we have not talked about yet: “getLogger”. This method is in the above example responsible for returning the “Logger” instance. If you have an application context or config this method’s implementation may look like this:

  1. return MyContext.getLogger();

As already mentioned you can also use the “LogManager” to get your logger.

  1. return LogManager.getLogger();

The only thing you must keep care of is that the “LogManager” is configured with the logger to return before its “LogManager.getLogger” method is invoked.

  1. LogManager.setLogger(new TraceLogger());

In the above case we are configuring the “LogManager” with a “TraceLogger” instance. “TraceLogger” simply formats all messages using the “LogMessage” class and “trace”s the formatted message. The formatted message includes for example the log level of the message. You just have to replace this single line of configuration code to make all your application code log to a new console. If you are for example switching from the Macromedia Compiler (MMC) to the Motion-Twin ActionScript 2.0 Compiler (MTASC) you may want to log with Flashout.

  1. LogManager.setLogger(new FlashoutLogger());

I said above that the “Logger” interface can also be used just as delegate and general interface for other logging APIs. Such a API is for example the ASCB Library Logging API. Joey Lott has written a great article about this API: Flash Logging - A Better Trace. To gain all strengths of this API you can configure the “LogManager” with the “AscbLogger”. The “AscbLogger” simply wraps a logger of the ASCB Library Logging API. You can thus do everything the same as Joey Lott explains it in his article. The only thing that differs are the calls to the log methods; you call them on an instance of the generic “Logger” interface and not on the ASCB Logger directly.

  1. LogManager.setLogger(new AscbLogger());

As you may have noticed there is a weakness with our way of configuring the “LogManager”. We always return the same logger. It would be great if we could for example return different loggers depending on different logger names. These loggers may also be part of a hierarchy and share their configuration, like the log level, with their children. As you may have guessed this functionalities can actually be achieved with no more than a few lines of configuration code. This is covered in the next tutorial page; just go on.

The Logger Repository

What exactly is a logger repository, how can I use it and what are its advantages compared to returning always the same logger?

A logger repository manages multiple loggers. The “LoggerRepository” declares only one method “getLogger(String):Logger”. This method returns a logger appropriate to the passed-in logger name. At its simplest form a “LoggerRepository” implementation just creates, stores and returns loggers by name. But a logger repository can also be more complex.

Let us begin with a simple logger repository, the AscbLoggerRepository. This repository does exactly the things I said when refering to the simplest form of repository. On invocation of the “getLogger” method it first looks whether a logger with the given name has already been stored. If so, this logger will be returned. Otherwise a new “AscbLogger” instance will be created, stored and then returned. As you can see there is not much magic going on. The great thing about this is that the returned loggers are configured with the given name and that this name is shown (by default) with the logged message. This makes it very easy to find out who logged which messages. We can of course still use the “LogManager” as on the previous tutorial page. We just have to configure it with the “AscbLoggerRepository”.

  1. LogManager.setLoggerRepository(new AscbLoggerRepository());

We also have to change the call to the static “LogManager.getLogger” method; we must include a name now. Note that you should decide at the beginning of the project whether to use named loggers or just one logger for the whole project. This largely depends on the size of your application. With small applications it may not be necessary to include a logger name in a message. You can also just always invoke the “LogManager.getLogger” method with a name regardless of whether the underlying configuration supports named loggers. This enables you to switch between the two styles of logger management as you please. You might also use a complex and slower logger management during production to be able to track down problems fast and a simple and fast logger management when the application is in use. The simple management may just ignore the given name. Following is the body of the “getLogger” method of the example of the previous tutorial page.

  1. return LogManager.getLogger("org.myproject.mymodule.MyClass");

A complex logger repository is the “LoggerHierarchy”. It manages loggers hierarchical. Every class that gets a logger passes in its name (as we did in the previous example) that is used as the logger’s name. This firstly clearly states which class logged which message in the log output and secondly makes up the hierarchical structure. A fully qualified class name consists of its namespace, that is its preceding package structure, plus the name of the class; for example “org.as2lib.env.except.Exception”. In this case the parent of the logger with full name “org.as2lib.env.except.Exception” is “org.as2lib.env.except” and the parent of this parent is “org.as2lib.env” and so on. It is now possible to configure every node in this hierarchical structure and its children will inherit this configuration unless they overwrite it. The call to the “LogManager.getLogger” method stays the same. Again, what has to be changed is the configuration.

  1. var repository:LoggerHierarchy = new LoggerHierarchy();
  2. var coreLogger:SimpleHierarchicalLogger = new SimpleHierarchicalLogger("org.myproject");
  3. coreLogger.addHandler(new TraceHandler());
  4. coreLogger.addHandler(new FlashoutHandler());
  5. repository.addLogger(coreLogger);
  6. var invisibleLogger:SimpleHierarchicalLogger = new SimpleHierarchicalLogger("org.myproject.invisiblemodule");
  7. invisibleLogger.setLevel(SimpleHierarchicalLogger.NONE);
  8. repository.addLogger(invisibleLogger);
  9. LogManager.setLoggerRepository(repository);

In the above configuration example we are first creating a new “LoggerHierarchy” instance. We are then instantiating two hierarchy able loggers, “coreLogger” and “invisibleLogger”, that are assigned to two nodes in the hierarchy, “org.myproject” and “org.myproject.invisiblemodule”, and adding these two loggers to the hierarchy. The hierarchy itself is set as repository on the “LogManager”. The “coreLogger” logs to the Flash Output Panel (”TraceHandler”) and to the Flashout Console (”FlashoutHandler”). The “invisibleLogger” inherits the handlers from its parent logger, the “coreLogger”, but it sets its log level to “NONE”. This means that it does not log messages.

If we now execute the following code, the message “This is an informative message.” will be logged to the Flash Output Panel and to the Flashout Console.

  1. LogManager.getLogger("org.myproject.mymodule.MyClass").info("This is an informative message.");

But if our class is somewhere in the “org.myproject.invisiblemodule” package or any sub-package no log output will be made.

  1. LogManager.getLogger("org.myproject.invisiblemodule.mysubpackage.MyOtherClass").info("This informative message will not be logged.");

It is important to note that with “SimpleHierarchicalLogger” instances the log level is, if not specified directly, the level of its parent and the handlers are all handlers of its parents. The children inherit the configuration of their parent or parents.

In the above example I have introduced a new notion: log handlers. The following tutorial page gives more insight into their usage.

The Log Handler

What are log handlers and why and when are they used.

A “LogHandler” is responsible for the actual logging of messages with some loggers. All loggers that offer hierarchy support for the “LoggerHierarchy” repository must work with log handlers, because otherwise inheriting the output devices of the parents would not be possible. Such a logger is the “SimpleHierarchicalLogger”. But log handlers are not only helpful in a hierarchy. Also non-hierarchical loggers like the “SimpleLogger” can benefit from log handlers. Log handlers enable a logger to support more than one output device. With log handlers one and the same logger can for example log to the Flash Output Panel and to a log file.

  1. var logger:SimpleLogger = new SimpleLogger("MyLogger");
  2. logger.addHandler(new TraceHandler());
  3. logger.addHandler(new MyCustomHandler());
  4.  

When you now invoke any log method on the above “SimpleLogger” instance it will dispatch a “LogMessage” instance containing all information about the actual message, like its level, to all regitered handlers that are then responsible for creating the string representation of the given “LogMessage” instance, probably via the “LogMessage.toString” method, and for writing this string to the actual output device.

As you can see, loggers supporting log handlers are much more flexible than the ones that do not, like “TraceLogger” that is hard-coded to log to the Flash Output Panel. But you must also see the downside of this approach: it costs performance, even if there is only one handler added. I thus recommend using loggers with handler support only in production and only if they are needed and switching to hard-coded loggers when the application is actually in use.

The Log Message

What does the log message and how can its string representation be customized?

There are some loggers that work with the “LogMessage” class; above all loggers that work with “LogHandler”s. A “LogMessage” simply holds all information regarding an actual log message. These information are the message itself, the level at which the message is logged, the name of the logger the message is logged on and the time at which the log message was created. To now actually log the message it must be stringified, this means a string representation of the message including all extra information must be generated. To do this you simply have to invoke the “LogMessage.toString” method. The string this method returns is generated through a stringifier that can be set via the “LogMessage.setStringifier” method. There also exists a customizable “LogMessage” stringifier, “PatternLogMessageStringifier”. You can use this stringifier or a custom one to format log messages as you please.

  1. <p>LogMessage.setStringifier(new PatternLogMessageStringifier(true, true, "HH:nn:ss.S"));</p>
Tuesday, May 31st, 2005

As2lib Event API

This tutorial covers event basics, different approaches to event handling, their advantages and disadvantages and how these approaches were realized in the As2lib Event API (org.as2lib.env.event).
Note that this article has also been published on the official as2lib homepage: As2lib Event API. You may find it better structured there. If you are familiar with the old already known event approaches you may just scroll down until the “Distributor” part.

Event Basics

What is an event? When are events used? Are events related to the Observer Design Pattern of GoF (Gang of Four)?

An event is every change in an application an event handler receives. Events are for example triggered if a certain point in an application is reached or if a user interacts with the application. In theory everything can trigger events. An event-driven API normally consists of two main modules.

  • A dispatcher is responsible for dispatching the event to multiple event handlers. Dispatchers are also called broadcasters, distributors, publishers and multicasters.
  • Event Handlers are responsible for handling events. They receive the event and operate in accordance with it. They may also consume the event and thus stop the event to be dispatched to further event handlers. Event handlers are also called listeners, subscribers and observers.

In most event APIs, event handlers can register themselves at a dispatcher. If an event occurs the dispatcher then dispatches the event to all registered event handlers. Thus the dispatcher has two main responsibilites.

  • Managing a collection of event handlers.
  • Dispatching events to all registered event handlers if they occur and maybe managing an event queue that holds unprocessed events.

Some dispatchers also support the consumption of events. The dispatcher then dispatches an event to event handlers step-by-step, until an event handler consumes the event. This is sometimes called to veto to an event.

Events are used when there is more than one object or there are an unknown number of objects that may want to be notified if some specific operation took place. For example if a user pressed a button. It is bad pracitice to use events for every interaction between objects because this adds unnecessary complexity and slows the execution of the program down. So, think carefully if using events really benefits your application in your specific case.

The Observer Design Pattern is closely related to events. The “Subject” is analog to the “Dispatcher” and the “Observer” to the “Event Handler”. Reading the Observer’s intent you can clearly see the connection: “Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.”.

Listener Source

What is a listener source? Which listener source implementations are provided by the As2lib Event API and what are their differences?

In the As2lib Event API we call event handlers listeners and thus a collection of event handlers listener source. The “EventListenerSource” interface declares common methods needed to manage a collection of listeners. A listener source is so to speak a data holder that holds listeners. The As2lib Event API offers two types of listener sources.

  • The “SimpleEventListenerSource” manages listeners in the simplest way possible.
  • The “TypeSafeEventListenerSource” manages listeners and ensures that it contains only listeners of a specific type.

Depending on your event handling implementation you may not need a type-safe listener source because you may have ensured the correct type of the listeners somehow else. In such a case use the simple listener source or the type-safe listener source with a flag that the type shall not be checked. Otherwise use the type-safe listener source because it prevents unexpected behavior caused by wrong listeners.

Broadcaster

What is the As2lib Broadcaster Event API (org.as2lib.env.event.broadcaster)? What are its advantages and disadvantages compared to other event APIs? In which scenarios is it worthwhile to use this API?

This As2lib Broadcaster Event API is built around the “EventBroadcaster” interface. This interface combines listener source and dispatch functionalities. The “dispatch” method expects exactly one parameter that must be of type “EventInfo”. This event info is also passed-to the listeners. It holds at least the name of the event that is also the name of the method to invoke on the listeners, the event method. It may also contain any additional information. The normal process when working with this API is as follows:

  • Creating a listener interface that declares the event method. This event method takes one parameter that must be of type “EventInfo” or a sub-interface or implementation-class.
    1. interface MyListener {
    2.     public function onMyEvent(myEventInfo:MyEventInfo):Void;
    3. }
    4.  
  • Creating the specific event info class that has been used as type for the event method’s parameter. This class must implement the “EventInfo” interface. The “getName” method should return a hard-coded string, the name of the event method.
    1. class MyEventInfo implements EventInfo {
    2.     private var information:String;
    3.     public function MyEventInfo(information:String) {
    4.         this.information = information;
    5.     }
    6.     public function getInformation(Void):String {
    7.         return this.information;
    8.     }
    9.     public function getName(Void):String {
    10.         return "onMyEvent";
    11.     }
    12. }
    13.  

In your application you instantiate an event broadcaster for every event, listeners can register to and implement the appropriate add and remove methods that should be properly typed to the listener interfaces. Now when an event occurs you instantiate the appropriate event info with all needed information and dispatch to the added listeners with the appropriate broadcaster passing-in the instantiated event info.

  1. class MyClass {
  2.     private var myBroadcaster:EventBroadcaster;
  3.     public function MyClass(Void) {
  4.         this.myBroadcaster = new SpeedEventBroadcaster();
  5.     }
  6.  
  7.     private function dispatchMyEvent(information:String):Void {
  8.         this.myBroadcaster.dispatch(new MyEventInfo(information));
  9.     }
  10.  
  11.     public function addMyListener(myListener:MyListener):Void {
  12.         this.myBroadcaster.addListener(myListener);
  13.     }
  14.  
  15.     public function removeMyListener(myListener:MyListener):Void {
  16.         this.myBroadcaster.removeListener(myListener);
  17.     }
  18. }

The advantages of this approach are:

  • If the event method’s name of a listener changes there is only one place to change the event method’s name, the event info class.
  • Because of your typed methods to add and remove listeners you ensure that only listeners that really implement the expected interface are added, so there is no need for further type-checking by the listener source, which leads to better performance.
  • Because of your event info class that contains all event specific information you are not bloating the event method’s parameter list. Note though that in the above case this would not have been necessary because we have only one information.
  • This approach is rather fast because it does not have to make any detours.
  • This approach does not prescribe the existence of a listener interface or class. Note that I myself think of not declaring an explicit listener type as bad practice.
  • If one of the listeners needs more information one can simply extend the event info without having to alter other listeners.

But there are also many disadvantages:

  • The compiler does not check whether the event method’s name on the listener and the one in the event info class are in sync.
  • The listener must always get its information from the passed-in event info. If there is no information needed or if the information needed is only one value, as in the above example, an event info is overkill and unintuitive.
  • The listener is bound or has a dependency to the As2lib Broadcaster Event API because it declares one parameter of type “EventInfo”.
  • There must be one, in some cases unnecessary class, the “EventInfo” implementation.

Let’s move on and take a look at the As2lib Multicaster Event API that eliminates some of the disadvantages, but adds some new ones.

Multicaster

What is the As2lib Multicaster Event API (org.as2lib.env.event.multicaster)? What are its advantages and disadvantages compared to other event APIs? In which scenarios is it worthwhile to use this API?

The core interface of the As2lib Multicaster Event API is the “EventMulticaster”. This interface combines, like the “EventBroadcaster” interface of the previous tutorial page, listener source and dispatch functionalities. The difference is that this time the “dispatch” method expects any number of parameters of any type that are passed-to the event methods of all registered listeners; the listener interface is thus not bound to an event info and a specific event API. All the rest stays basically the same.

  1. interface MyListener {
  2.     public function onMyEvent(information:String):Void;
  3. }

  1. class MyClass {
  2.     private var myMulticaster:EventMulticaster;
  3.     public function MyClass(Void) {
  4.         this.myMulticaster = new SimpleEventMulticaster("onMyEvent");
  5.     }
  6.  
  7.     private function dispatchMyEvent(information:String):Void {
  8.         this.myMulticaster.dispatch(information);
  9.     }
  10.  
  11.     …
  12. }

The advantages are:

  • We do not bind our listeners anymore to a specific event API by allowing custom parameters.
  • We can still package information/parameters into one data transfer object if we want to, but it is not prescribed. And this object also does not have to extend or implement a specific class or interface.
  • Dispatching events and declaring listeners feels much more natural and is more intutitive.

But again this is connected with some disadvantages:

  • The name of the event method is in most implementations passed-in on instantiation of every event multicaster. This is more error-prone because it is easy to make a typo. If the listener’s event method’s name changes we also have to change the event method name in multiple locations.
  • Compile-time checking of whether the event method’s name on the listener and the ones we pass-to our event multicasters is still not given.

Again, there issstill much that can be improved; most notably the lack of compile-time checking. This issue is addressed by the As2lib Distributor Event API, so go on.

Distributor

What is the As2lib Distributor Event API (org.as2lib.env.event.distributor)? What are its advantages and disadvantages compared to other event APIs? In which scenarios is it worthwhile to use this API?

Unlike the previously discussed event APIs, this API’s base interface is not a dispatcher but a dispatcher control: “EventDistributorControl”. This interface acts as a listener source and a provider and controller of a typed distributor. You again create such a control per event providing it the listener interface or class.

  1. var myControl:EventDistributorControl = new SimpleEventDistributorControl(MyListener);

You can then use this control to add and remove listeners.

  1. myControl.addListener(new SimpleListener());
  2. myControl.addListener(new ComplexListener());

If the event occurs you first get a typed distributor from the control and invoke the event method on it passing-in the appropriate parameters. The neat thing about this is that you get compile-time checking whether the method you are invoking exists and whether the parameters are of correct type.

  1. var distributor:MyListener = myControl.getDistributor();
  2. distributor.onMyEvent("myArg1", 2, new Object());

The advantages are obvious:

  • You have proper compile-time checking when dispatching the event; type-checking of parameters and existence-checking of method.
  • You can have proper type-checking of listeners because you specified the expected listener type.
  • You can use custom parameters and are not bound to an event info. But if you want to package the information in one object you are free to do so; this object must not extend or implement any specific class or interface, its just a design choice.
  • The listener is not bound to any specific event API.

One disadvantage may be that if the name of the event method on the listener changes you have to make a change in every place where you dispatch events. But the compiler tells you that and where these changes have to be made. A good IDE also changes all calls to the method automatically if you change the method’s name. So, this is not much of an issue.

Tuesday, May 31st, 2005

As2lib 0.9 Release

I am proud to finally announce a new release of the As2lib after 9 months of hard work to make it faster, lighter, more stable and more intuitive.

Download As2lib Version 0.9.

The most important changes are:

  • Mtasc and Flex compatibility
  • Unit tests for the whole framework to make it much more stable
  • Modularization of the framework to reduce file size when only using a sub-set of its different parts
  • Good api documentation and easy online access

The most noticeable features are:

  • An event api split into three parts to serve different preferences and needs - org.as2lib.env.event
  • An exception api that gives comprehensive information about the problem through a detail message, an exception cause and a stack trace - org.as2lib.env.except
  • An improved logging api that is easy to use and fast; It allows for easy inclusion of other logging apis and consoles - org.as2lib.env.log
  • Better overloading capabilities of the overloading api - org.as2lib.env.overload
  • Fast and easy to use reflection api - org.as2lib.env.reflect
  • A comprehensive set of data holders/structures and iterators needed in daily work - org.as2lib.data.holder
  • Different mainly numerical type formats - org.as2lib.data.type
  • A local connection api that allows for return values and even sends exceptions over the wire - org.as2lib.io.conn.local
  • A unit testing api that allows for easy asynchronous testing - org.as2lib.test.unit
  • A mock object api that can be easily integrated with the unit testing api - org.as2lib.test.mock
  • A speed test api (profiler) - org.as2lib.test.speed
  • Support for processes and easy configuration - org.as2lib.app
  • Different utilities for strings, objects, classes, arrays and a date formatter that do simple but often needed tasks - org.as2lib.util

I hope you will enjoy the new features and give much feedback.

Tuesday, April 12th, 2005

ActionScript 2 Coding Standards: The Method

Developers who work in teams should write their code in the same manner. This helps developers to easily read and refactor code of others. It is thus necessary to define code conventions or coding standards programmers can act in accordance with.
The following coding standards regarding methods arose during my work on the As2lib Framework. I wanna thank Alex Uhlmann and Martin Heidegger for reading the whole document and giving valuable feedback on how to improve it.

Download as PDF: ActionScript 2 Coding Standards - The Method.

Method Documentation

Example:

  1. /**
  2. * Returns an array that contains the methods represented by {@link MethodInfo}
  3. * instances, this type and super types’ declare, that are not filtered/excluded.
  4. *
  5. * <p>The {@link TypeMemberFilter#filter} method is invoked for every
  6. * method to determine whether it shall be contained in the result. The
  7. * passed-in argument is of type {@code MethodInfo}.
  8. *
  9. * <p>If the passed-in method filter is {@code null} or {@code undefined}
  10. * the result of an invocation of the {@link #getMethodsByFlag} method
  11. * with argument {@code false} will be returned.
  12. *
  13. * <p>{@code null} will be returned if:
  14. * <ul>
  15. *   <li>The {@link #getType} method returns {@code null} or {@code undefined}.</li>
  16. *   <li>
  17. *     The {@link #getMethodsByFlag} method returns {@code null} or {@code undefined}.
  18. *   </li>
  19. * </ul>
  20. *
  21. * @param methodFilter the filter that filters unwanted methods out
  22. * @return an array containing the declared methods that are not filtered,
  23. * an empty array if no methods are declared or all were filtered or null
  24. * @see #getMethodsByFlag
  25. */
  26. public function getMethodsByFilter(methodFilter:TypeMemberFilter):Array {
  27.     if (!getType()) return null;
  28.     if (!methodFilter) return getMethodsByFlag(false);
  29.     var result:Array = getMethodsByFlag(methodFilter.filterSuperTypes());
  30.     for (var i:Number = 0; i < result.length; i++) {
  31.         if (methodFilter.filter(result[i])) {
  32.             result.splice(i, 1);
  33.             i–;
  34.         }
  35.     }
  36.     return result;
  37. }

Constitution: The method documentation begins with the begin-comment delimiter “/**” and ends with the end-comment delimiter “*/”. Both comment delimiters are placed on their own line. Every line in-between has a leading asterisk character “*” which is indented with a blank space to line it up. The line length of the documentation text should be limited to 80 characters, regardless of the preceding indention and asterisk character. The method documentation is composed of a main description followed by a tag section. Both these parts are allowed to contain in-line tags.

Good Example:

  1. /**
  2. * [Main Description]
  3. *
  4. * [Tag Section]
  5. */

Bad Example:

  1. /** [Main Description]
  2. * [Tag Section]
  3. */

or:

  1. /**
  2. *
  3. *[Main Description]
  4.  
  5. * [Tag Section]
  6. */

Position and Alignment: The method documentation precedes the method declaration and is indented to align with it. There is no blank line between the documentation and the declaration.

Good Example:

  1. /**
  2. * ..
  3. */
  4. public function getName(Void):String;

Bad Example:

  1. /**
  2. * ..
  3. */
  4.  
  5.     public function getName(Void):String;

Speech: Write the description in 3rd person declarative (descriptive) rather than 2nd person imperative (prescriptive).

Good Example:

  1. Returns an array that ..

Bad Example:

  1. Return an array that ..

When you refer to the instance created from the current class use “this” instead of “the”.

Good Example:

  1. Returns the log level of this logger.

Bad Example:

  1. Returns the log level of the logger.

Avoid Latin. This means use “for example” instead of “e.g.”, “also known as” instead of “aka” and so on.
Main Description: The main description starts in the line after the begin-comment delimiter. The first sentence of it is a short summary, starting with a verb, that contains a concise but complete description of the method. The summary is followed by a blank line and paragraphs, separated by “<p>” tags that provide more information about the method. There is a blank line before every “<p>” tag. Document special cases like what happens if a specific parameter is “null” or if a dependent method returns “null”. Try to be as concise as possible so that developers are able to use the method extensively by only reading its documentation. You should nevertheless try to write the description as implementation-independent as possible and specify dependencies only where necessary, because the documentation defines a contract the method and any over-writing or implementing methods must meet. That means the more implementation-specific the contract the harder it is to refactor the method without violating it.

Good Example:

  1. /**
  2. * Short summary sentence that starts with a verb.
  3. *
  4. * <p>Paragraph one that is separated by a {@code <p>} tag. It may span
  5. * multiple lines and contain multiple sentences. Limit the line length
  6. * to 80 characters.
  7. *
  8. * <p>Paragraph two that is also separated by a {@code <p>} tag.
  9. *
  10. * <p>{@code null} will be returned if:
  11. * <ul>
  12. *   <li>Case one.</li>
  13. *   <li>Case two.</li>
  14. *   <li>Case three.</li>
  15. * </ul>
  16. *
  17. * [Tag Section]
  18. */

Bad Example:

  1. /**
  2. * Long description of what the method does. It is not concise but just says about every-
  3. * thing in an awkward manner. The line length exceeds 80 characters.
  4. * <p>There is no blank line before the <p> tag. This makes reading the documentation
  5. * more difficult. In-line code is not enclosed in {@code} tags. Special cases are not
  6. * mentioned.
  7. * [Tag Section]
  8. */

Tag Section: The tag section is separated from the main description by a blank line. It starts with the first occurrence of an “@” character that institutes a block tag. This section provides information about parameters “@param”, return values “@return” and exceptions “@throws” of the method. It also contains “@see” tags to point to references, like dependent methods.

Good Example:

  1. /**
  2. * [Main Description]
  3. *
  4. * @param parameterOne the first parameter that ..
  5. * @param parameterTwo the second parameter that ..
  6. * @return the ..
  7. * @throws IllegalArgumentException if {@code parameterOne} is {@code null}
  8. * @see #doSomethingSimilar
  9. * @see org.as2lib.env.reflect.ClassInfo#forInstance
  10. */
  11. public function doSomething(parameterOne:String, parameterTwo:Number):String;

Bad Example:

  1. /**
  2. * [Main Description]
  3. *
  4. * @param parameterTwo the second parameter that ..
  5. * @return the ..
  6. * @see org.as2lib.env.reflect.ClassInfo#forInstance
  7. */
  8. public function doSomething(parameterOne:String, parameterTwo:Number):String;

Block Tags Ordering: Include block tags in the following order:

  1. @param parameter-name description
  2. @return description
  3. @throws class-name description
  4. @see package.class#member label
  5. @see <a href="url#value">label</a>
  6. @see "string"
  7. @deprecated deprecated-text

@param parameter-name description: “@param” tags are ordered the same as the parameters in your method declaration. “parameter-name” is the name of the parameter in your method declaration. “description” is a partial sentence that describes the parameter. It starts with a lower case word and does not end with a period. It is allowed to span multiple lines. If the parameter does not have to be passed-in, that means is optional, put the “(optional)” text after “parameter-name” and before “description”.

Example:

  1. @param [parameter-name] (optional) [description]

If the method expects an unknown number of parameters signal this in your documentation with an “@param” tag and “..” as “parameter-name”. Add a description as for any other parameter. If the parameters must be of a specific type mention that type in your description.

Example:

  1. @param .. [description]

@return description: Include “@return” tags for all methods that do not return “Void” and that are not constructors. “description” is a partial sentence that describes the return type and permissible range of values. It starts with a lower case word and does not end with a period. Is is allowed to span multiple lines. Supply return values for special cases whenever possible. Such a special case is for example returning “null”.

@throws class-name description: “class-name” is the name of the exception that may be thrown by the method. “description” is a partial sentence that says in which cases the exception is thrown. It starts with a lower case word and does not end with a period. It is allowed to span multiple lines. This tag is very important and must not be forgotten because if not specified there is no possibility of knowing what exceptions are thrown and in which cases besides searching in the whole method implementation for “throws” clauses directly or getting an exception at run-time.

Example:

  1. @throws UnknownOverloadHandlerException if no adequate overload handler was found

@see reference: Take a look at the “@see” tag description on http://java.sun.com/j2se/1.5.0/docs/tooldocs/windows/javadoc.html#@see and at how to order “@see” tags on http://java.sun.com/j2se/javadoc/writingdoccomments/#multiple@see.

In-Line Tags: As said before, both the main description and the tag section are allowed to contain in-line tags. In-line tags appear within curly braces as “{@tag}”. They can be used anywhere that text is allowed. Common in-line tags are “{@link}” to point to a reference and “{@code}” to highlight code.

Example:

  1. The {@link TypeMemberFilter#filter} method is invoked ..

or:

  1. If the passed-in method filter is {@code null} or ..

If you refer to or highlight a method omit the parentheses and use only the method name. This makes reading the resulting method documentation more natural and easy.

Good Example:

  1. The {@link TypeMemberFilter#filter} method is invoked ..

Bad Example:

  1. The {@link TypeMemberFilter#filter(MethodFilter):Boolean} method is invoked ..

{@link package.type#member label}: Inserts an in-line link with visible text “label” that points to the documentation for the specified package, type or member of a referenced type. The label is optional. If you do not specify it the API name will be used. Do not add a link to all API names in your documentation. Because links call attention to themselves it can make the documentation more difficult to read if used profusely. Link the same API name in one method documentation only ones. You may also only highlight the API names using the “{@code}” tag and provide “@see” tags for them in the tag section.

Example:

  1. The {@link #getMethodsByFlag} method returns ..

{@code text}: Displays text in code font. Use “{@code}” tags to highlight API names. These include parameter names and constants like “null” or “undefined” as well as class, interface, package, property, variable and method names.

Example:

  1. {@code null} will be returned if ..

<code>text</code>: Displays text in code font. Use this html tag if the code spans multiple lines and stands on its own, that means is not directly included in a sentence. The opening tag “<code>” is followed by a return. The following code is indented with two spaces. Do also not use tabs in your code but replace them with four spaces. The closing tag “</code>” is placed on its own line and is aligned with its corresponding opening tag. It ends the code.

Example:

  1. <code>
  2.   var server:LocalServer = new LocalServer("local.as2lib.org");
  3.   server.putService("myServiceOne", new MyServiceOne());
  4.   server.putService("myServiceTwo", new MyServiceTwo());
  5.   server.run();
  6. </code>

or:

  1. <code>
  2.   var client:LocalClientServiceProxy = new LocalClientServiceProxy("local.as2lib.org/myService");
  3.   var callback:MethodInvocationCallback = client.invoke("myMethod", ["p1", "p2"]);
  4.   callback.onReturn = function(returnInfo:MethodInvocationReturnInfo):Void {
  5.       trace("myMethod - return value: " + returnInfo.getReturnValue());
  6.   }
  7.   callback.onError = function(errorInfo:MethodInvocationErrorInfo):Void {
  8.       trace("myMethod - error: " + errorInfo.getException());
  9.   }
  10. </code>

Method Declaration

Constitution: Methods are declared in classes and interfaces. The method declaration is the part of the method before the curly braces. It consists of the access specifier “public” or “private”, the optional scoping specifier “static”, the statement “function”, the name of the method, the parameter list and the return type. Interfaces can only declare methods, while classes must also provide implementations for them.

Example of Method Declaration and Implementation in Class:

  1. public static function getLogger(loggerName:String):Logger {
  2.     // Implementation omitted
  3. }

Example of Method Declaration in Interface:

  1. public function isDebugEnabled(Void):Boolean;

Line Length: The line length of the method declaration, as well as any other code, should be no longer than 120 characters. A word-wrap should be made somewhere between 80 and 120 characters, when the maximal line length is exceeded. This makes it possible to print the code and to view it with almost any editor. The line after the word-wrap is indented by 8 blank spaces to visually separate it from the method body. (As you can see in the example it is damn hard to have a method declaration that exceeds the maximal line length. If you have such a method you maybe did something else wrong. ;))

Example:

  1. public static function doSomethingVeryVeryVeryInteresting(argumentOne:ReallyInterestingClass, argumentTwo:ArgumentTwo,
  2.         argumentThree:String, argumentFour:Number):String {

Method Visibility or Method Access

Public: “public” is the easiest access specifier. Public methods can be accessed from anywhere in your application.

Private: “private” is the most restrictive access level. A private member is accessible only to the class in which it is declared and to any sub-classes.

Usage: Hide methods as much as possible. The fewer methods that are public, the cleaner a class is and the easier it will be to test, use and refactor. The public methods that a class exposes will often be only the methods of the interfaces it implements and methods needed for configuration, often getter and setter methods.
Do always declare the visibility of a method even if it is public which is the default case in ActionScript 2 and could simply be neglected. The problem with not declaring the visibility is that developers who read the code must know that the default visibility is public and that it makes the code more difficult to read.

Good Example:

  1. public function getLevel(Void):LogLevel;

Bad Example:

  1. function getLevel(Void):LogLevel;

Method Scope

Per Instance: Per instance methods are the default case. You do not have to declare methods as being per instance using any special keyword. Such methods exist per instance. This means that you invoke them on the instance of a class and not on a class itself. You therefor must create an instance of a class first and you can then invoke methods on that instance. Try to use per instance methods as often as possible and avoid per class methods. Per instance methods are much more flexible and open you the door to Design Patterns and key Object Oriented Programming (OOP) concepts like polymorphism.
It is possible to use the “this” keyword to refer to the instance the method is invoked on.

Example:

  1. public function doSomething(Void):Void {
  2.     trace("The method ,doSomething’ is invoked on instance: " + this);
  3. }

Per Class (Static): Per class methods are also known as static methods. You declare them using the statement “static”. This statement is placed after the visibility and before the “function” statement. Per class methods exist per class and not per instance. This means that you invoke them directly on the class. It is not possible to invoke them on instances. You reference the method using the class name plus the method name. Only per instance or per class methods declared in the same class as the per class method can invoke it directly by its name without having to specify the class. Do not overuse per class methods. Use them only if they are really appropriate because there are some problems associated with their usage:
- Per class methods can neither be declared nor implemented on interfaces.
- All callers are tied to the class that declares the per class method and to the per class method itself.
- It is not possible to override per class methods.
- Per class methods hold state in per class variables. This means that the state is shared among all callers in the whole application and that the state can be changed from everywhere.
As you can see, using per class methods makes your code less flexible and error-prone when the per class methods hold state. Therefore think twice before using per class methods in your code.
A useful and appropriate per class method is for example the “org.as2lib.env.log.LogManager.getLogger(loggerName:String):Logger” method. This method is responsible for returning loggers from one and the same repository for every class in the whole application that needs one. It does not really do any functionality but just forwards all the work to the repository that has been set prior to starting the application.
It is not possible to use the “this” keyword directly in per class methods because using it causes a compiler error. It is nevertheless necessary in some cases to use this keyword to refer to the class the method is declared and implemented on. To do this you can use the “eval” method as follows: eval(”th” + “is”).

Example:

  1. public static function doSomething(Void):Void {
  2.     trace("The static method ,doSomething’ is invoked on class: " + eval("th" + "is"));
  3. }

Method Name

Formation: A method name starts with a verb. This verb describes in an abstract manner what the method does. It is possibly followed by other words that describe the function of the method more clearly. Take a look at the sections Accessor Methods and Mutator Methods for more information on common verb prefixes.

Good Example:

  1. public function loadXmlData(xmlDataSource:String):Void;

Bad Example:

  1. public function xmlData(xmlDataSource:String):Void;

Upper and Lower Case: The first word of a method name is written lower case. Following qualifying words start with upper case letters. When you use abbreviations like XML in the method name, write only the first letter in upper case. If the abbreviation exists only of two letters think about not using it and writing it out. Do for example not use the abbreviation MC for MovieClip in method names.

Good Example:

  1. public function loadXmlData(xmlDataSource:String):Void;

or:

  1. public function createMovieClipLoader(Void):MovieClipLoader;

Bad Example:

  1. public function loadXMLData(xMLDataSource:String):Void;

or:

  1. public function createMcLoader(Void):MovieClipLoader;

Suffixes: Do not use single suffixes that reference to parameters like “to”, “than” and “by” in method names.

Good Example:

  1. public function isMoreExplicit(overloadHandler:OverloadHandler):Boolean;

or:

  1. public function apply(object);

or:

  1. public function move(x:Number, y:Number):Void;

Bad Example:

  1. public function isMoreExplicitThan(overloadHandler:OverloadHandler):Boolean;

or:

  1. public function applyTo(object);

or:

  1. public function moveBy(x:Number, y:Number):Void;

Plural or Singular: Method names are allowed to be plural. That means if you are returning a collection of elements name your method with a word in plural. I do not advocate the use of the collection type in method names. The type is already declared as return type in the method declaration and should not also be in the method name.

Good Example:

  1. public function getAllListeners(Void):Array;

Bad Example:

  1. public function getAllListenerArray(Void):Array;

or:

  1. public function getListenerArray(Void):Array;

or:

  1. public function getAllListener(Void):Array;

Language: Write method names in American English to avoid a mix of different languages and to ensure that every developer can read the method names properly. Using only English words also guarantees that every alphabetical letter needed is allowed to be part of a method’s name.

Statement: A method name should be self-explanatory. To meet this goal do not use abbreviations. Use them only if they are common and you are really sure that the developers who use your code know their meaning. Do not forget to provide the written out form of your abbreviation in the documentation.
You should also qualify the method name with as much information as needed to make it self-explanatory and to leave no space for misinterpretation. Imagine for example a “BeanDefinition” class. This class is responsible for holding information about beans. This is for example the name of the bean and the class of the bean. While you could name the two accessor methods for these information “getName” and “getClass” one could misinterpret them and think “getName” returns the name of the definition. The same is true for “getClass”. But if you qualify the method names and name them “getBeanName” and “getBeanClass”, is is almost impossible to misinterpret them, and the four characters you have to write more will not hurt you. This said, do not over-generalize method names, but provide all necessary information.
As said above, a method name should be self-explanatory. Take for example a method named “loadXmlData”. This name implies that something is loaded from outside of the flash environment, that this something is data and that this data is encoded in XML format. The fact that XML data is loaded from outside of the flash environment also implies that the loading might fail and that it might be necessary to pay attention to error handling in form of a “null” return value, an exception or the invocation of an “onError” event method.

Good Example:

  1. public function loadXmlData(xmlDataSource:String):XmlLoaderCallback;

Bad Example:

  1. public function load(source:String):XmlLoaderCallback;

Method Parameter List

Declaration: The parameter list is a comma delimited list that is declared after the method name. The list starts with an opening brace “(” and ends with a closing brace “)”. The opening brace is put directly after the method name, with no blank space in-between. The individual parameters are declared between the braces and separated by commas. There is a blank space after every comma, that is before every new parameter. Every parameter has a name and a type. The type is declared using ActionScript’s 2 strict data typing post-colon syntax, “myParameter:MyType”.

Good Example:

  1. public function log(message:String, level:LogLevel):Void;

Bad Example:

  1. public function log ( message,level ):Void;

or:

  1. public function log(message : String,level : LogLevel) : Void;

Name: The name of a parameter should be descriptive and self-explanatory. It should also specify the context of the parameter.

Good Example:

  1. public function loadXmlData(xmlDataSource:String):Void;

Bad Example:

  1. public function loadXmlData(source:String):Void;

You may use the prefix “new” for parameters of setter methods.

Example:

  1. public function setName(newName:String):Void;

As with method names the name of a parameter should be written in American English to avoid a mix of different languages and assure that every developer in your team can read the method names properly.

Usage: Due to the sloppy nature and some bugs of ActionScript 2 regarding parameters we must be careful of how to use them.
A method normally declares all parameters and their types in its parameter list.

Example:

  1. /**
  2. * [Main Description]
  3. *
  4. * @param level the level to check whether this logger is enabled for it
  5. * @return true if this logger is enabled for the passed-in {@code level}
  6. * else false
  7. */
  8. public function isEnabled(level:LogLevel):Boolean;

But if you expect a parameter of any type, do not use “Object” as type but leave the parameter type empty. Do this because the Macromedia compiler thinks that instances casted to interfaces are not of type “Object”. In this case you may also think about using a marker interface as type. This is an interface that does not declare any methods.

Example:

  1. /**
  2. * [Main Description]
  3. *
  4. * @param target the target object to stringify
  5. * @return the string representation of the passed-in {@code target}
  6. * object
  7. */
  8. public function stringify(target):String;

If the method expects no parameters do not just leave the parameter list empty, because this could also imply that the method expects an unknown number of parameters, but specify “Void” as parameter.

Example:

  1. /**
  2. * [Main Description]
  3. *
  4. * @return the name of this bean
  5. */
  6. public function getBeanName(Void):String;

If the method expects an unknown number of parameters, including zero parameters, leave the parameter list empty. This is for example the case with an overloading method that forwards the functionality to other methods depending on the passed-in parameters.

Example Normal Method:

  1. /**
  2. * [Main Description]
  3. *
  4. * @param .. any number of values, that are of the expected type that was
  5. * specified on construction, to concat with this array
  6. * @return an array that contains the values of this array as well as the
  7. * passed-in ones
  8. */
  9. public function concat():TypedArray;

Example Overload Method:

  1. /**
  2. * @overload getBeanByName
  3. * @overload getBeanByNameAndType
  4. */
  5. public function getBean();
  6.  
  7. /**
  8. * [Main Description]
  9. *
  10. * @param beanName the name of the bean to return
  11. * @return the bean corresponding to the passed-in {@code beanName}
  12. */
  13. public function getBeanByName(beanName:String);
  14.  
  15. /**
  16. * [Main Description]
  17. *
  18. * @param beanName the name of the bean to return
  19. * @param beanType the expected type of the bean to return
  20. * @return the bean corresponding to the passed-in {@code beanName} that
  21. * is of the expected {@code beanType}
  22. */
  23. public function getBeanByNameAndType(beanName:String, beanType:Function);

If the method expects an unknown number of parameters, but at least one declare one parameter in the parameter list. Also declare the type of the parameter if known.

Example with Known Type:

  1. /**
  2. * [Main Description]
  3. *
  4. * @param neededParameter [description]
  5. * @param .. any number of parameters that are of type {@code String}
  6. * and [further description]
  7. */
  8. public function doSomething(neededParameter:String):Void;

Example with Unknown Type:

  1. /**
  2. * [Main Description]
  3. *
  4. * @param value the new value, that is of the expected type specified on
  5. * construction, to add to the end of this array
  6. * @param .. any number of values, that are of the expected type specified
  7. * on construction, to add to the end of this array
  8. * @return the new length of this array
  9. */
  10. public function push(value):Number;

Do not forget to comment all of the above special cases clearly in your method documentation, as it is done in the examples.

Method Return Type

Always declare the return type of the method. If the method does not return any value declare “Void” as return type.

Example:

  1. /**
  2. * [Main Description]
  3. *
  4. * @return the name of this bean
  5. */
  6. public function getBeanName(Void):String;
  7.  
  8. /**
  9. * [Main Description]
  10. *
  11. * @param beanName the new name of this bean
  12. */
  13. public function setBeanName(beanName:String):Void;

In case the return type can be any type do not use “Object” as return type but leave the return type signature empty. Do this because the Macromedia compiler thinks that instances casted to interfaces are not of type “Object”.

Example:

  1. /**
  2. * [Main Description]
  3. *
  4. * @return the message object to log
  5. */
  6. public function getMessage(Void);

Method Body or Method Implementation

Constitution: The method body is implemented using curly braces. The opening curly brace is on the same line as the method declaration. The curly brace is separated from the declaration by a blank space. The first line of code of the implementation is on the next line and indented by four blank spaces. The last line contains only the closing curly brace that is aligned to the beginning of the method declaration.

Good Example:

  1. public function getBeanName(Void):String {
  2.     return beanName;
  3. }

Bad Example:

  1. public function getBeanName(Void):String{
  2. return beanName;
  3. }

or:

  1. public function getBeanName(Void):String {
  2.     return beanName;
  3.     }

or:

  1. public function getBeanName(Void):String
  2. {
  3.     return beanName;
  4. }

Error Handling: Every method should implement its own error handling. That means it must be possible to invoke every method, public or private, directly and that special cases like “null” parameters must be handled correctly even if dependent methods were overridden by a sub-class. Do thus not make your error handling dependent on other methods.

“super”: Within method bodies you can use the “super” operator to explicitely invoke methods of super-classes. While it is possible to use “super” every time you invoke methods of super-classes, use it only if you have overridden a super-class’s method and you want to invoke the overriden method.

Line Length: As with the method declaration, the line lenght should be limited to 120 characters. If your code exceeds this maximal line length, make a word-wrap somewhere between 80 and 120 characters. This makes it possible to print the code and to view it with most editors. The code after the word-wrap is indented by 8 blank spaces to visually separate it from following code and to affiliation to the preceding line.

Example:

  1. throw new IllegalArgumentException("The types of the arguments [" + arguments + "] must match one of the two choices.",
  2.         this, arguments);

Method Response

Return Value: The method can response to an invocation by returning a value. This value can for example be the result of a computation or can inform the caller of the success or failure of the invocation. Use return values for the latter case only in low-level APIs. It is recommended to use exceptions in high-level APIs because they provide much more information about what exactly went wrong and where it went wrong.
A metod returns a value using the “return” statement. This statement is followed by a blank space and the value to return. If the value is computed using operators then put the computation into parenthesis. Do not use parenthesis if you create an instance to return using the “new” operator.

Good Example:

  1. return size;

or:

  1. retrun (size != null ? size : defaultSize);

or:

  1. return new MyClass();

Bad Example:

  1. return (size);

or:

  1. return size != null ? size : defaultSize;

or:

  1. return (new MyClass());

or:

  1. return(size != null ? size : defaultSize);

Exceptions: Throw exceptions to indicate that something went wrong. Distinguish between normal and fatal exceptions. Normal exceptions are likely to be catched and handled by the application, while fatal exceptions indicate programming errors that must be handled by application developers. Fatal exceptions are for example “org.as2lib.env.except.IllegalArgumentException” that is thrown on illegal arguments and “org.as2lib.env.except.AbstractOperationException” that is thrown if the developer forgot to overwrite an abstract method. A normal exception is “org.as2lib.io.conn.core.client.UnknownServiceException” that is thrown if a service is not available right now. The application can catch this exception and for example try to reconnect after waiting one second. Exceptions are thrown using the “throw” statement. Do not use parenthesis after the “throw” statement.

Good Example:

  1. throw new MyException("A descriptive message.", this, arguments);

Bad Example:

  1. throw(new MyException("A descriptive message.", this, arguments);

A complete article about error handling will be available soon.

Logging: A method may also response by logging. This kind of response differs from the two above in that it is only intended for developers and does not affect the execution of the application. The method may response at error or fatal level if it catched a normal exception or fatal exception. It may log a warning if it is in a state that should not actually occur or something unexpected happened that poses no problem to the execution of the application. The method logs at info and debug level to inform developers of what it is going to do. All this logging helps developers to easily track down errors of the application. An article about logging with the As2lib Logging API will be available soon.

Constructor Method

A constructor is responsible for creating and initializing instances of a class. Constructors must have the same name as the class that declares it. Constructor names do thus not start with a verb and do also not start with a lower case letter but with an upper case one, like class names. Constructors do also not declare a return type, because the return type is always the class that declares the constructor.

Example:

  1. public function ClassInfo(clazz:Function, name:String, package:PackageInfo) {
  2.     this.clazz = clazz;
  3.     this.name = name;
  4.     this.package = package;
  5. }

You do not have to declare a constructor for a class. If you do not do it the compiler automatically creates an empty constructor. It is suggested to always declare a constructor for every class you create whether it performs any functionality or not. Declaring the constructor makes you think about which parameters the super-class’s constructor expects and makes you implement your constructor the way that the expected parameters are passed-to the super-class’s constructor. Otherwise the compiler creates a new constructor that expects no parameters and all parameters are “undefined” when the super-class’s constructor is invoked. Note that the super-class’s constructor is always invoked whether you invoke it by hand using “super” in the constructor body or not. But with using “super” by hand you have the possibility to pass parameters to the super class’s constructor.

Example with Super-Class’s Constructor that Expects No Parameters:

  1. public function ClassAlgorithm(Void) {
  2. }

Example with Super-Class’s Constructor that Expects Four Parameters:

  1. public function ConstructorInfo(constructor:Function, declaringClass:ClassInfo) {
  2.     super (NAME, constructor, declaringClass, false);
  3. }

Accessor Methods

Naming: Accessor methods are usually named after what they return.

Getter Methods: Getter methods are accessor methods that return non-boolean values. Their names start with the verb “get”. These methods normally do not do much computation but just look up a variable so they can be assumed to execute fast.

Example:

  1. public function getBeanName(Void):String {
  2.     return this.beanName;
  3. }

Test Methods: Test methods are accessor methods that return boolean values. These methods normally give information about the object by testing its state or indicate whether some specific operations shall take place. Common prefixes are forms of to be like “is” and “was”, “has”, “can”, “should” and “will”.

Example:

  1. public function isRootPackage(Void):Boolean {
  2.     return (getParentPackage() == null);
  3. }

or:

  1. public function hasListeners(Void):Boolean {
  2.     return (this.listeners.length > 0);
  3. }

or:

  1. public function canEvaluate(Void):Boolean;
  2. public function shouldAbort(Void):Boolean;
  3. public function willTerminate(Void):Boolean;

Creation Methods: Creation methods are accessor methods that create and return a new instance. Use the verb “create” for their names. Do not use “new” because it is not a verb and because it is easy to mistake it for a real instantiation of a class in a method call.

Good Example:

  1. public function createLogger(loggerName:String):Logger {
  2.     return new SimpleLogger(loggerName);
  3. }

Bad Example:

  1. public function newLogger(loggerName:String):Logger {
  2.     return new SimpleLogger(loggerName);
  3. }

Search Methods: Search methods are accessor methods that search for something and return either the result or return “null” or throw an exception. Use the verb “find” for the names of these methods. Search methods can be assumed to need some time for execution.

Example:

  1. public function findBeansOfType(beanType:Function):Array {
  2.     var result:Array = new Array();
  3.     for (var i:Number = 0; i < this.beans.length; i++) {
  4.         if (beans[i] instanceof beanType) {
  5.             result.push(beans[i]);
  6.         }
  7.     }
  8.     if (getParentBeanFactory()) {
  9.         result = result.concat(getParentBeanFactory().findBeansOfType(beanType));
  10.     }
  11.     return result;
  12. }

Computation Methods: Computation methods are accessor methods that compute something and return the result. They are normally used to do arithmetic calculations. You prefix them with the verb “compute”.

Example:

  1. public function computeSomething(parameter1:Number, parameter2:String):Number;

Generation Methods: Generation methods are similar to computation methods. The difference is that they are used for any kind of generation, while computation methods are mostly used for arithmetic calculations. Generation methods are prefixed with the verb “generate”.

Example:

  1. public function generateResponseServiceUrl(serviceUrl:String, methodName:String):String;

Conversion Methods: Conversion methods convert objects and return the result. The most common conversion method is “toString” that creates a string representation of the object it is invoked on. As you can see conversion methods are prefixed with the adverb “to”.

Example:

  1. public function toArray(Void):Array {
  2.     return this.data.concat();
  3. }

or:

  1. public function toString(Void):String {
  2.     return ("[type " + ReflectUtil.getTypeName(this) + "]");
  3. }

Mutator Methods

Naming: Mutator methods are usually named after what they mutate or after what they do.

Setter Methods: Setter methods are mutator methods that simply set an instance or class variable. They can thus be supposed to execute rather fast. Prefix their method name with the verb “set” and their parameter’s name with the adjective “new”. Setter methods can be invoked as often as you please.

Example:

  1. public function setName(newName:String):Void {
  2.     this.name = newName;
  3. }

Initialization Methods: Initialization methods are mutator methods that initialize or establish an object or a concept, for example a whole instance or just a variable. Prefix their names with the verbs “init” or “initialize” or “setup”. Initialization methods can normally only be invoked ones. For example a method with name “initName” can be supposed to do the same as a simple setter method with the difference that it for example throws an “org.as2lib.env.except.IllegalStateException” if invoked more than ones.

Example:

  1. public function initName(name:String):Void {
  2.     if (!name) {
  3.         throw new IllegalArgumentException("Parameter ,name’ [" + name + "] must not be ,null’, ,undefined’ or an empty string.", this, arguments);
  4.     }
  5.     if (this.name != null) {
  6.         throw new IllegalStateException("The name [" + this.name + "] has already been initialized.", this, arguments);
  7.     }
  8.     this.name = name;
  9. }

Collection Methods: Collection methods are mutator methods that add values to, remove values from or get collections. Do not indicate a specific ordering of the internal collection with your collection methods’ prefixes if the ordering is not necessary. Without a specific order you have more freedoms of how to implement the method.
Prefix a collection method that assigns a value to a key “put”. This term does not indicate any special value ordering.

Example:

  1. public function putLogger(loggerName:String, logger:Logger):Void;

Use the prefix “add” if values are just added to the collection. This term does also not indicate any special value ordering.

Example:

  1. public function addListener(listener:Listener):Void;

Use the prefix “remove” to remove values from the collection.

Example:

  1. public function removeListener(listener:Listener):Void;

The prefix “push” is used if values are added to the end of a collection and “pop” if values are removed from the end of a collection. Note that these two prefixes are used in conjunction and ensure the last-in, first-out policy, that gets implemented by stacks.

Example:

  1. public function push(value):Void;

or:

  1. public function pop(Void);

If values are removed from the beginning of a collection use the prefix “dequeue”. Use the prefix “enqueue” in conjunction with this method to add values to the end of the queue. These two prefixes are, as already indicatd, used in conjunction. They ensure the first-in, first-out policy, that gets implemented by queues.

Example:

  1. public function enqueue(value):Void;

or:

  1. public function dequeue(Void);

Use the prefix “getAll” if all values of a collection are returned. Try to return the values in a general collection type like an array, regardless of the internal type. Almost all collections provide functionalities to convert them into arrays. This gives your more freedoms for your internal implementation. Most developers are also more familiar with working with an array than with a list or a queue. Do never return the internal collection itself, but return a copy of it or make it immutable before you return it. Otherwise it would be possible to edit the internal collection from outside, which easily leads to unexpected behavior.

Example:

  1. public function getAllListeners(Void):Array {
  2.     return this.listeners.toArray().concat();
  3. }

Abstract Methods

Introduction: Abstract methods are methods that do not provide an implementation but only a declaration. You can compare them with method declarations in an interface. The difference is that abstract methods are declared in abstract classes, that are classes that implement some methods of for example an interface and leave some other methods abstract. The concrete sub-class is then responsible for implementing these abstract methods.

Declaration: Abstract methods are not supported by ActionScript 2. But you can simply throw an exception of type “org.as2lib.env.except.AbstractOperationException” to mark the method as being abstract and to get a run-time exception if you forgot to overwrite the abstract method. Because this is actually only a work-around do only use it if their usage does really improve your code.
If the abstract method declares a return type you must return something to please the compiler, even if the “return” clause is not reachable due to the “throw” clause. In such a case do always return “null”, as in the example.

Example:

  1. private function doLoad(dataSource:String):String {
  2.     throw new AbstractOperationException("This method is marked as abstract and must be overriden.", this, arguments);
  3.     return null;
  4. }

Naming: When an abstract class implements a method that does some general operations and forwards others to an abstract method and, you cannot find a name that describes the functionality of that abstract method more clearly than the name of the forwarding method, name this abstract method like the forwarding method with the prefix “do”.

Example:

  1. public function load(dataSource:String):String {
  2.     if (loadedFiles.contains(dataSource)) {
  3.         return loaddedFiles.get(dataSource);
  4.     }
  5.     var result:String = doLoad(dataSource);
  6.     loadedFiles.put(dataSource, result);
  7.     return result;
  8. }
  9.  
  10. private function doLoad(dataSource:String):String {
  11.     throw new AbstractOperationException("This method is marked as abstract and must be overridden.", this, arguments);
  12. }

Overload Methods

Introduction: Overloading is not supported by ActionScript 2. That means it is not possible to declare multiple methods with the same name that only differ in their parameter list or return type.

Naming: A naming convention is needed that gives information about the actual functionality and about the specific way of performing this functionality. This is done using the actual name of the method plus the adverb “by” or the prepositions “for” or “of” plus the base names of the parameters, without the context part, separated by the conjunction “And”. If the method that is forwarded to takes no parameters use “Void” as parameter name, “getMethodsByVoid(Void):Array”. Do not qualify the actual name of the most common method by “by”, “for” or “of” or use the actual name for the method that forwards to the qualified methods.

Forwarding: Forwarding can be done depending on the length of the parameter list or the types of the parameters or both using if-else statements. While this is possible it is awkward to implement for every forwarding method and it is likely that special cases are ignored. I thus suggest you to use the As2lib Overloading API which does all the overloading for you and provides clean error handling.

Visibility or Access Specifier: The forwarding method as well as the qualified methods that are forwarded to have all the same access specifier. That means if the forwarding method is public, the qualified methods are public too. This ensures that each method can be invoked directly without the overhead produced by the forwarding. Note that with overloaded constructors the qualified constructors are always private, because otherwise they could be invoked directly.

Parameter List: The forwarding method declares no parameters in its parameter list.

Return Type: The forwarding method declares a return type if all methods it forwards to also declare the same return type. If the return type of the methods it forwards to differ declare no return type.

Example: If the method to overload has the name “getBean” and the method can either be invoked using the name and the expected type of the bean or using only the bean’s name, name your forwarding method “getBean” and the two other methods “getBeanByNameAndType” and “getBeanByName” respectively.

Example using the As2lib Overloading API

  1. /**
  2. * @overload getBeanByName
  3. * @overload getBeanByNameAndType
  4. */
  5. public function getBean() {
  6.     var o:Overload = new Overload(this);
  7.     o.addHandler([String], getBeanByName);
  8.     o.addHandler([String, Function], getBeanByNameAndType);
  9.     return o.forward(arguments);
  10. }
  11.  
  12. /**
  13. * [Main Description]
  14. *
  15. * @param beanName the name of the bean to return
  16. * @return the bean corresponding to the passed-in {@code beanName}
  17. */
  18. public function getBeanByName(beanName:String) {
  19.     ..
  20. }
  21.  
  22. /**
  23. * [Main Description]
  24. *
  25. * @param beanName the name of the bean to return
  26. * @param beanType the expected type of the bean to return
  27. * @return the bean corresponding to the passed-in {@code beanName} that
  28. * is of the expected {@code beanType}
  29. */
  30. public function getBeanByNameAndType(beanName:String, beanType:Function) {
  31.     ..
  32. }

Further Example:

  1. public function getBeansOfType(beanType:Function):Array;

or:

  1. public function getBeanNamesForType(beanType:Function):Array;

Event Methods

Event Methods are invoked when an event is broadcasted. They capture events. Event methods are implemented on listeners, that can be registered at broadcasters. A common prefix for event methods is “on”. Macromedia itself is following this convention as you can for example see at all the MovieClip events like “onEnterFrame” and “onRelease”. An article about the As2lib Event Handling API will be available soon.

Anonymous Methods

Introduction: Avoid the use of anonymous methods as often as possible. There are some major drawbacks with their usage. The compiler does for example not verfiy the parameters and the return type in the method declaration, which may lead to huge problems when the type signature of the method declaration is changed in the declaring class and you do not get a compiler error for your anonymous methods.

Declaration: Anonymous methods are simply functions created at run-time that are assigned to a variable.

Example:

  1. handler.invoke = function(proxy, methodName:String, args:Array) {
  2.     ..
  3. };

or:

  1. var invoke:Function = function(proxy, methodName:String, args:Array) {
  2.     ..
  3. };

Note the semicolon at the end of the variable initialization.

Access: As said above anonymous methods are functions that are assigned to a variable. You therfore access them using the variable’s name. They are invoked like any other method.

Example:

  1. handler.invoke(myProxy, "doSomething", ["parameter1", 2]);

or:

  1. invoke(myProxy, "doSomething", ["parameter1", 2]);

Usage: Anonymous methods are mostly used for event handling and anonymous classes. They can be assigned for example to events on movieclips like “onEnterFrame”.

Example:

  1. myMovieClip.onEnterFrame = function(Void):Void {
  2.     ..
  3. };

Yet a much cleaner way is to extend the MovieClip class and declare the instance method “onEnterFrame” in this class.

Example:

  1. class org.simonwacker.myapplication.view.MyMovieClip extends MovieClip {
  2.     ..
  3.     private function onEnterFrame(Void):Void {
  4.         ..
  5.     }
  6.     ..
  7. }

As I said they are also used for anonymous classes. Take the “org.as2lib.io.conn.core.event.MethodInvocationReturnListener” for example. It prescribes that sub-classes must implement exactly one method “onReturn”. Let us say you call six remote methods and the return value of every of these methods must be handled in a different way. In such a case you may consider to use anonymous implementations of the “MethodInvocationReturnListener” class. You simply instantiate the interface (note that this does not work in Flex directly) and initialize the “onReturn” method with an anonymous method for every of the six remote method invocations. This saves you from creating six classes that maybe only do some trivial operations.

Example (not Flex compatible):

  1. var returnListener:MethodInvocationReturnListener = new MethodInvocationReturnListener();
  2. returnListener.onReturn = function(returnInfo:MethodInvocationReturnInfo):Void {
  3.     ..
  4. };

Example (Flex compatible):

  1. var temporaryReturnListener = new Object();
  2. temporaryReturnListener.__proto__ = MethodInvocationReturnListener.prototype;
  3. var returnListener:MethodInvocationReturnListener = temporaryReturnListener;
  4. returnListener.onReturn = function(returnInfo:MethodInvocationReturnInfo):Void {
  5.     ..
  6. };

“toString” Method

Every class should implement a “toString” method that returns at least the name of the class. This helps alot with diagnostics and is especially helpful in generating log messages, that help in debugging. It is even better if the “toString” method returns the name of the class as well as its current state. A common convention of how to generate string representations of classes is the following:

Minimal Syntax:

  1. [type fullyQualifiedClassName]

Syntax with State:

  1. [type fullyQualifiedClassName
  2.   variable1: value1
  3.   variable2: value2
  4. ]

You can extend the class “BasicClass” of the As2lib Framework to inherit a “toString” method that returns the name of your class, as in the first example. If you want to do the stringification on your own the As2lib Reflection API might help you, especially the light-weight “org.as2lib.env.reflect.ReflectUtil” class.

Method Call

When calling a method there is no blank space between the method name and the parenthesis. There is also neither a blank space between the opening brace and the first parameter nor between the last parameter and the closing brace. Parameters are separated by commas. There is a blank space after every comma but not before it.

Example:

  1. myXmlLoader.loadXmlData("myXmlFile", myResponseListener);

If the parameters are too long or if there are too many parameters, build parameter groups and put each group on its own line. Indent each line to align with the above parameter group.

Example:

  1. myXmlLoader.loadXmlData("myXmlFile", myResponseListener,
  2.                         myTimeout, myAlternativeXmlFile,
  3.                         myUnnecessaryFlag);