ActionScript
Rosetta.as
// Copyright (c) 2007 Art & Logic, Inc. All Rights Reserved.
// $Id: $
// Above: [sf02], [sf03]
// a note on the stuff inside the square brackets:
// Each of the codes inside brackets is a reference to the rule(s) in the
// Style Guide that's being illustrated, as follows:
// [sf##] - StyleGuide/SourceFiles
// [cas##] - StyleGuide/ClassesAndStructures
// [nr##] - StyleGuide/NamingRules
// [cg##] - StyleGuide/Commenting
// [fmt##] - StyleGuide/Formatting
// Throughout: [fmt01]; all indentation done with space characters (0x20),
// three spaces per logical tab stop.
// [nr21] C/C++ guard to prevent recursive inclusion of header files
// !!! ActionScript3 - no preprocessor syntax !!!
// [nr25] the 'q' prefix indicates that we are controlling conditional
// !!! ActionScript3 - no precompilation macros !!!
// [sf07], only one class declared/defined in this source file.
// General Note about coding in Flex:
//
// Unlike most languages, Flex applications are written with a sometimes
// arbitrary combination of ActionScript3 and MXML. One can write a GUI class
// definition using ActionScript3 (Rosetta.as), or as an mxml file (RosettaView.mxml),
// with an accompanying script block (to implement the classes functions). In
// general, mxml is used for more static GUI components, and can often be thought
// of as the "View" portion of a component. It generally makes sense to implement
// at least some of the control logic in the mxml file, since one will be referencing
// objects identified in the mxml. This is how the Adobe examples are
// implemented. The Flex compiler actually converts/compiles the mxml into a class
// definition, so aside from the language peculiarities, there is no functional
// difference between a class defined in mxml vs. ActionScript3 (though there are some
// limitiations when defining a class using MXML - see Adobe documentation). One can
// inherit from a class defined in mxml, or extend in mxml a class defined in ActionScript3.
// This leaves the developer with a powerful myriad of choices about how to implement
// a class hierarchy.
//
// It's very possible and probable that this will be used in ways that some developers
// may not like. There are no absolutes, but as a general rule it's a good idea to
// keep ActionScript3 code in an MXML file to a reasonable minimum. This will encourage
// a clean separation of View code from control logic. It also has other benefits, such
// as keeping the static markup portion of the code, which may be more relevant to a
// graphic designer, separate from the C-style ActionScript3 code, which may confuse them.
//
// There are various techniques for organizing code in this way. A couple of good
// possibilities are a "code-behind" pattern, where the application control logic is
// implemented as a base class which the MXML extends. Alternatively, the bulk of the
// application control logic could extend the MXML.
package com.artlogic.devpractices.rosetta
{
// [sf05] Generally best to import individual classes
import flash.filesystem.File;
import mx.logging.Log;
import mx.logging.LogEventLevel;
/**
* Rosetta
* Simple, but meaningless class to demonstrate Art+Logic programming
* style conventions as they are applied to the various programming languages
* we use. In several places, you're asked to imagine a block of useful code
* to avoid distracting you from the issues of formatting (etc.) that are being
* demonstrated.
*
* As you've already seen above, the thing about to be shown in this file is
* called out in comments using the A+L-style markup '!!!'
*
* [nr11], [nr18] class names prefixed with "A"
* !!! ActionScript3 - Unlike some other languages, the ActionScript3
* compiler expects the classname to match the filename. So classnames should
* not be prefixed with an 'A'. !!!
*
* [cas01] public member functions/methods listed first
* !!! ActionScript3 - Should be listed after Constructor function for clarity !!!
*/
public class Rosetta
{
// [nr08] new non-class types (enumerations, data structures, etc) are
// named with MixedCase and no special prefix
// [nr23] identifiers specifying constants for the program are always
// prefixed with a lowercase 'k'.
// !!! ActionScript3 - no enumeration syntax, constants declared as follows. !!!
public const kSomeConstant = 50;
public const kSomeOtherConstant = 100;
/**
* Construct the Rosetta object.
* !!! ActionScript3 - Although arguments can be passed into a constructor to
* initialize the object, it is usually better to have no arguments to the
* constructor, and use properties to initialize the object. This allows use
* of the object from MXML and makes serialization/deserialization of the
* object easier. !!!
*/
public function Rosetta()
{
}
// !!! ActionScript3 - No destructors in ActionScript3 !!!
// [cas01] public methods (functions) are declared first.
// !!! ActionScript3 - it's best to declare public properties before
//public functions.
// Note! Use explicit get and set properties, rather than creating
// functions like setFilename and getFilename in ActionScript3.
/**
* Set the filename to be used by this object.
* @param filename The filename.
*/
public function set filename (filename:String):void
{
fFilename = filename;
fInputFile = new File(filename);
}
/**
* [nr07] Member functions are named using CamelCase, with a leading
* upper case letter.
* !!! ActionScript3 - Like java, class methods
* (called functions in ActionScript3), begin with a lowercase letter. !!!
*/
public function memberFunctionWithMultipleWords():Boolean
{
var ok:Boolean = true;
// [fmt08] -- formatting of a for() loop.
for (var i:Number = 0; i < kSomeConstant; ++i)
{
for (var j:Number = 0; j < kSomeOtherConstant; ++j)
{
// [fmt02] -- preprocessor directives go in column zero.
// !!! ActionScript3 - No preprocessor directives. There ActionScript3 compiler does
// however support conditional compilation, controlled from build tool. !!!
// Note use of logger for verbosity control. Further initialization
// of the Log would be needed to actually use it this way...
Log.getLogger("Rosetta").log(LogEventLevel.INFO,
"testing: " + i + ", " + j);
// [nr07] -- Calls to member functions of the current object are
// made through an explicit this pointer.
ok = this.test(i, j);
if (!ok)
{
Log.getLogger("Rosetta").log(LogEventLevel.ERROR,
"Fatal error testing: " + i + ", " + j);
break;
}
}
if (!ok)
{
break;
}
}
return ok;
// [fmt02] -- labels used as the target of a goto are placed in column zero.
// !!! ActionScript3 - No goto or labels !!!
}
/**
* !!! Here's how we handle very long lines (i.e., longer than 79
* characters)
*/
public function longLineWrapping():Boolean
{
// [fmt03] -- source lines should be less than 80 characters long.
// [fmt04] -- break lines at the most logical point
// 1 2 3 4 5 6 7
//34567890123456789012345678901234567890123456789012345678901234567890123456789
var retval:Boolean = SomeStaticClass.FunctionWithManyArguments(
longArgumentName1, longArgumentName2, longArgumentName3,
longArgumentName4);
// alternative format #1
retval = SomeStaticClass.FunctionWithManyArguments(longArgumentName1,
longArgumentName2,
longArgumentName3,
longArgumentName4);
// alternative format #2
retval = SomeStaticClass.FunctionWithManyArguments(
longArgumentName1,
longArgumentName2,
longArgumentName3,
longArgumentName4);
// [fmt05] -- prefer to break after a comma
retval = SomeStaticClass.SomeUsefulFunction(x.leftMargin(),
x.topMargin() + x.bottomMargin() - 1);
// [fmt05] -- if that's not possible, break after an operator
retval = SomeStaticClass.SomeUsefulFunction(x.LeftMargin() +
anObject.HorizontalPadding(), x.TopMargin() + x.BottomMargin() - 1);
return retVal;
}
/**
* !!! Loop formatting
*/
public function loops():void
{
// [fmt08] -- for() loop; note the pre-incremented form of the loop control
for (var i:Number = 0; i < kSomeConstant; ++i)
{
// loop code here...
}
// [fmt09]
while (someBoolean)
{
// useful code...
}
// [fmt09] -- while loop with an empty body makes the emptiness explicit
while (this.someFuncReturningBool())
{
// intentionally empty...
}
// [fmt10] -- do/while loop.
// [fmt18] -- exception to the bracing rule saying that braces should
// always be on a line by themselves
do
{
// loop code here..
} while (someBoolean);
}
// [cas01] Protected member functions declared after public
/**
* [nr03] parameters passed to functions are named using lowerCamelCase
*/
protected function test(firstVal:Number, secondVal:Number):Boolean
{
var retval:Boolean = true;
// [fmt07] -- if/else formatting
if (firstVal < secondVal)
{
// [cg06] -- use '$$$' as a marker to indicate a request that the
// following block of code be reviewed before a change is considered
// complete
// $$$ before
// this.process(secondVal);
// $$$ after
// Shouldn't this be processing the first value in this case?
this.process(firstVal);
// $$$ end
}
else if (secondVal > kSomeConstant)
{
this.process(secondVal);
}
else
{
// [fmt18] -- use curly braces even in cases where the language doesn't
// require it to prevent maintenance errors.
retval = false;
}
return retval;
}
// [cas01] Private member function declared after protected
/**
* Useful function to process something or other.
*/
private function process(val:Number):void
{
// [cg05] -- use '!!!' as a marker to indicate a section of code that
// requires attention
// !!!... removing old hardcoded value
// fProcessResult = 131;
// ...!!!
var result:Number = val;
while (--val)
{
result *= val;
}
fProcessResult = result;
}
/**
* Demonstrating the formatting of a switch statement.
*/
private function decide(midiEventType:Number):void
{
// [fmt11] -- switch/case formatting.
switch (midiEventType)
{
// comments precede the case
case kNoteOn:
this.noteOn(now);
if (duration > 0)
{
this.noteOff(now + duration);
}
break;
case kNoteOff:
this.noteOff(now);
break;
case kPolyAfter:
after = channelValue;
// [fmt35] include, but comment out the break when falling through intentionally.
// break;
case kChannelAfter:
this.afterTouch(after);
break;
// [fmt36] no need to include a commented out break when falling through an empty case
case kPitchbend:
// blank line...
// [fmt12] -- switch statements should always have a default case.
default:
// !!! ActionScript3 - Currently no native support for asserts.
// One could implement assert as a runtime error, and use the
// DEBUG configuration flag to compile out the implementation
// but there would be a performance hit. !!!
//ASSERT(false);
Log.getLogger("Rosetta").log(LogEventLevel.ERROR,
"Unsupported event type!");
break;
}
}
// [cas02] member variables declared in the same order: public, protected, private.
// [nr04], [nr19] Member variables are prefixed with a lowercase 'f', followed
// by an uppercase letter.
// !!! ActionScript3 - Applies only to protected and private variables. Public
// variables have no prefix, to enable later conversion to get/set function
// in a way that is transparent to clients of this class.
public var modValue:Number;
// [nr04], [nr19] Member variables are prefixed with a lowercase 'f', followed
// by an uppercase letter.
// !!! ActionScript3 - Applies only to protected and private variables. Public
// variables have no prefix, see above.
private var fFilename:String;
// [fmt28] -- when declaring pointer variables, attach the '*' to the
// typename, not the variable name.
// !!! ActionScript3 - All variables are "pointers" in ActionScript3 !!! //
private var fInputFile:File;
private var fProcessResult:Number = 0;
}
}
RosettaView.mxml
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (c) 2007 Art & Logic, Inc. All Rights Reserved.
$Id: $
Above: [sf02], [sf03]
a note on the stuff inside the square brackets:
Each of the codes inside brackets is a reference to the rule(s) in the
Style Guide that's being illustrated, as follows:
[sf##] - StyleGuide/SourceFiles
[cas##] - StyleGuide/ClassesAndStructures
[nr##] - StyleGuide/NamingRules
[cg##] - StyleGuide/Commenting
[fmt##] - StyleGuide/Formatting
Throughout: [fmt01]; all indentation done with space characters (0x20),
three spaces per logical tab stop.
This is a simple, and meaningless View component showing some of the style guide
formatting appropriate for MXML. Wherever it makes sense to do so, apply
ActionScript3 syntax to MXML.
-->
<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" width="400" height="300">
<mx:Script>
<![CDATA[
import com.artlogic.devpractices.rosetta.Rosetta;
// !!! ActionScript3/MXML - Formatting inside a script block should be treated
// identically to formatting in an ActionScript file. See Rosetta.as for
// more info.
/**
* Handle click event.
*/
private function onDoItClick(event:Event):void
{
// [nr07] -- Calls to member functions of the current object are
// made through an explicit this pointer.
// !!! ActionScript3 - This applies to public properties as well. All id'd
// members in MXML are defined as public properties.
var filename:String = this.filenameInput.text;
if ((null != filename) && (filename.length > 0))
{
//Create a Rosetta object
if (null == fRosetta)
{
fRosetta = new Rosetta();
}
//Initialize the filename property
fRosetta.filename = filename;
//Do something with the object.
fRosetta.memberFunctionWithMultipleWords();
}
}
// [nr04], [nr19] Member variables are prefixed with a lowercase 'f', followed
// by an uppercase letter.
private var fRosetta:Rosetta;
]]>
</mx:Script>
<mx:Text text="Enter filename below:"/>
<!-- [nr04], [nr19] Member variables are prefixed with a lowercase 'f', followed
by an uppercase letter.
!!! MXML - Public properties do not require (and should not have) an 'f' prefix.
Members defined in MXML are by definition public. !!!
-->
<mx:TextInput id="filenameInput" width="379"/>
<!--[nr07] -- Calls to member functions of the current object are
made through an explicit this pointer.
!!! MXML - Use the this. syntax when referencing member functions and
variables. !!!
-->
<mx:Button label="Do it!" width="161" click="this.onDoItClick(event)"/>
</mx:Panel>