Introduction

You are currently browsing the weblog archives for April, 2005.

Archive for April, 2005

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: